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

Node.js fetch 发送流时报 duplex option is required

流式请求正文需把duplex字符串与body一起传给实际fetch入口。核对ReadableStream类型和封装选项,再走原上传流程验证内容与结果。

把 ReadableStream 作为请求正文传给 Node.js 内置 fetch 或 Request,出现 RequestInit: duplex option is required when sending a body。这个错误可能在构造请求时就抛出,请求尚未进入网络连接阶段,先检查请求选项和 body 类型。

Node.js 18.13.0 已有这种检查;Node.js 24.10.0 内置的 Undici 实现同样要求这类流式正文显式提供 duplex。它不能概括成每个 fetch 实现、每种正文类型都有相同配置要求。

与流一起传入字符串 half

下面是已有流 uploadStream 和目标地址 uploadUrl 的请求构造片段;这两个变量应来自应用原来的上传流程:

const request = new Request(uploadUrl, {
  method: 'POST',
  body: uploadStream,
  duplex: 'half',
});

随后由应用原来的请求流程发送这个 Request。直接调用 fetch 时,也把同一个选项放在包含 method 和 body 的选项对象中,不放进 headers,也不加到流对象上。

这里的值是字符串 'half'。duplex: true 不是有效替代值;不要按布尔开关理解它。在上述内置实现中,接受的 duplex 枚举值只有 half。这个参数名称也不能用来推断 Node 必须先发完整个上传才会处理响应,或用来承诺服务端的并发收发行为。

Undici 的 fetch 还支持以 AsyncIterable 作为流式正文,这条路径也需要相应的 duplex 设置。应用如果导入的是第三方 node-fetch、直接使用 undici.request,或通过框架包装请求,先确认实际 API 和安装版本,不能照搬内置 fetch 的选项约定。

选项可能在封装里丢了

错误仍存在时,沿实际调用链检查最终提交的对象:封装是否只复制了 method、headers、body,是否后来又覆盖了选项,是否另一个 new Request 没有收到 duplex。

字符串、经过 JSON.stringify 的 JSON 等已缓冲正文,不是本文所说的流式正文分支。不要把“发送 POST 都需要 duplex”写成应用规则。浏览器与 Node 的流式上传支持也有差异,跨环境封装应明确选择各自支持的入口。

流可能已经被读取或锁定,复用后还会遇到另一类正文可用性错误。增加 duplex 不会恢复已消耗的流;也不要为了重试把一次性上传流交给多个并发请求。重试需要应用原有的可重建正文、幂等约定和截止时间。

构造通过后,用原上传流程验收

在可控的上传目标和测试数据上验证请求不再于构造阶段报错,再确认服务端收到的内容、长度与业务结果。保留原来的取消、超时和错误处理,不用完整缓冲大文件来绕开一个选项问题。

如果此后报 fetch failed,应读取 cause 继续判断 DNS、连接、TLS 等条件;地址族连接尝试超时与这个构造错误不同。不能仅凭 duplex 报错消失认定上传已经成功。

修正封装后再从同一个应用入口验收,避免只在独立片段里加了参数,生产调用仍漏传。失败时恢复原请求代码与审定版本,核对已发起上传的业务状态,不自动重复提交可能已经到达服务端的数据。