DEBUG.WIKI
京东云;让 AI 进入真实业务;零售 · 物流 · 客服 · 硬件;查看方案 →
ZgoCloud:多地区部署,按需选节点;日本、美国、香港、德国
Evoxt;最高 6.0GHz CPU;高频云服务器 · 免费每周备份;查看配置 →

Bun test 报 EBADF

Bun test 遍历大量生成目录后,macOS 子进程报 EBADF 或没有输出。定位旧扫描器的目录描述符保留,核对 1.4.1 修复与实际执行版本。

同一个脚本用 bun run 能正常启动子进程,放进 bun test 后却出现 EBADF、空输出或等待不到 stdout。macOS 项目如果有大量生成目录,尤其是构建移动端后增加了很多 ios/Pods 子目录,应检查测试发现阶段是否保留了目录文件描述符。

这里针对 Bun 旧版本的扫描缺陷:已有相应表现的版本包括 1.3.4、1.3.10 至 1.3.14,以及 1.4.0。正式 1.4.1 的扫描实现已有清理修复,当前 1.4.3 也包含该处理。若使用更新后的版本仍失败,需要重新检查实际执行的二进制和故障条件,不能直接认定修复无效。

失败点可能在启动子进程之前

测试发现过程会遍历项目目录。受影响实现把扫描时打开的目录描述符保留到进程结束,因此描述符数量随目录数量增加。目录里没有测试文件,也可能进入遍历;被 Git 忽略的生成目录仍然存在于文件系统中。

macOS 的高编号描述符还可能触发子进程重定向的系统限制。于是问题最终表现为捕获子进程输出失败,看起来像测试命令或 stdout 管道出了问题。Linux 上也可能有目录描述符保留,但不能照搬 macOS 的同一数值边界或失败表现。

如果项目从未生成移动端文件时测试正常,构建一次后大量失败,先比较目录树的变化和执行环境,而不是先改每个测试的断言。显式传入一个测试文件路径也不能保证旧扫描器不会遍历其他目录。

先确认版本,再复查原有测试

在 CI 和本地分别检查实际运行版本。下面第二行的路径是示例,应换成项目里已有、会启动并捕获子进程输出的测试:

bun --version
bun test ./tests/spawn.test.ts

对于确认受影响的部署,按项目既有安装方式切换到包含修复的正式版本,再在同一份目录树上运行原测试。1.4.1 的扫描器已改为按目录引用生命周期释放句柄,非热重载测试也不再为遍历长期保存这些描述符。

升级后要确认 CI 缓存、容器镜像和 PATH 实际选中了新版本。只更新配置里的一行版本号、却继续运行缓存中的旧二进制,无法验证处理是否生效。

提高 Linux 的 inotify 上限不处理这个 macOS 子进程问题。它与文件监听配额不是同一根因;把受影响测试的输出改成忽略,也只是改变失败条件,不能证明扫描器释放了描述符。

看原本的输出是否恢复

验收应保留那份会触发问题的目录树,检查已有测试是否能完成、子进程退出状态是否正确、原本期待的 stdout 是否完整。若现有监控能观察描述符数量,还应确认它不再随每个扫描目录持续累积。

不要只以“测试没有打印 EBADF”判断成功。旧表现也包括退出失败但没有 error、空输出,以及等待输出的测试一直挂起。若这些症状仍在,记录实际 Bun 版本、操作系统、启动方式和输出捕获方式,再把问题与普通的子进程命令失败分开排查。