Node openAsBlob 报 ERR_INVALID_ARG_VALUE,实际可能只是文件不存在
Node 26.7.0 openAsBlob 会把 stat 失败变成 ERR_INVALID_ARG_VALUE。核对真实路径、正式错误码传播与同步异常,区分后续 Blob 读取失败。
openAsBlob('./input.txt') 抛出 TypeError [ERR_INVALID_ARG_VALUE]: Unable to open file as blob,并不总是 path 的 JavaScript 类型写错。Node.js 26.7.0 会把部分底层 stat 失败折成这个通用错误,真实问题可能是当前工作目录下没有该文件。
这项错误身份丢失与“返回 Promise 的接口是否同步抛错”是两件事。Node 26.12.1、24.22.1 的正式原生实现已经保留 stat 错误码,但它们对应的 JavaScript 包装仍有同步调用阶段,错误处理不能只依赖在调用结果上接 .catch()。
先保存实际解析后的路径
相对路径跟随进程工作目录,而不是自动跟随当前源文件。服务启动器、测试运行器或容器改变 cwd 后,同一字符串可能指向另一位置。使用应用原定的路径基准,记录脱敏后的实际路径及运行时版本,不要为了消掉错误把所有文件名都硬编码成某台机器的绝对路径。
在支持原生 ESM 的已有文件中,可把资源路径明确关联到模块,并同时覆盖同步错误和 Promise 拒绝:
import { openAsBlob } from 'node:fs';
try {
const blob = await openAsBlob(new URL('./input.txt', import.meta.url));
const bytes = await blob.arrayBuffer();
console.log(bytes.byteLength);
} catch (error) {
console.error(error.code, error.syscall, error.message);
}
这里 input.txt 应是该模块旁真正需要读取的资源,不是要求所有应用都改成模块相对路径。文件内容也可能敏感,诊断只记录必要信息,不输出完整文件。
旧实现得到空的文件条目后统一抛参数错误,没有把 libuv 的失败状态传出来。正式 26.12.1 和 24.22.1 会用这个状态构造原生系统错误,缺失文件能够表现为 ENOENT,并带有 stat 与 path 信息。它改善的是定位能力,不会帮应用创建丢失文件,也不能由此断言所有旧维护版本均已回补。
创建 Blob 成功,不等于文件已经可读完
这条原生修正主要覆盖 stat 阶段的失败,例如 ENOENT、ENOTDIR、ELOOP。不能把一个文件设为不可读后,期待创建 Blob 时就一定收到 EACCES:只要父目录可遍历,stat 仍可能成功,之后读取 Blob 才出现 NotReadableError。
同样,指向目录的路径在相关实现里可以先创建 Blob,稍后读取失败。需要普通文件的业务应按自己的文件类型契约检查;不能把错误码传播修复理解为已经拒绝所有目录。
openAsBlob 依赖磁盘文件,创建后文件被修改也会使读取失败。先检查是不是上传前又覆盖、截断或替换了源文件,而不是在每次 NotReadableError 后重新创建 Blob 并自动重试。提前 stat 或 access 也不能消除检查后文件发生变化的竞态,真正的读取调用仍需处理错误。
验证时分别覆盖原来的有效文件、缺失路径和读取期间文件变化。有效文件应得到预期字节;缺失路径应进入应用的缺文件分支,而不是被当成错误参数;后续读取失败仍要被捕获。保留 Node 与部署产物的版本记录,避免只在独立终端换了运行时,正式服务仍由旧二进制启动。


