DEBUG.WIKI
Evoxt;最高 6.0GHz CPU;高频云服务器 · 免费每周备份;查看配置 →
PanstarCloud:部署应用,接入 AI,云服务器与 AI 网关
京东云;让 AI 进入真实业务;零售 · 物流 · 客服 · 硬件;查看方案 →

Undici 8.7 上传 FormData 一直等到 AbortError,检查对象来自哪一份实现

Undici 8.7.0 request 使用 Node 全局 FormData 可挂起到取消。配套同一份 FormData、request 与 Agent,区分网络超时和较新流实现的边界。

JSON 请求正常,multipart 上传却长时间没有结果,最后只报 AbortError。如果代码调用外部包的 undici.request(),body 却用 Node 全局的 new FormData() 创建,要先检查两者是否来自同一份实现。

Undici 8.7.0 的这条组合会出现上传挂起:客户端先按对象结构把它当成 FormData,真正编码时却要求本包的 FormData 身份,两次判断不一致。这个现象也在 Node 24.14.0、Windows 11、显式使用同份 Agent 的条件下出现;不能只归为网络慢或目标服务器没响应。

同名 FormData 可能不被编码器承认

Node 内置 fetch、npm 安装的 Undici,以及依赖树里的另一份 Undici,可以各自拥有 FormData。它们都有 append 等方法,不等于能通过同一编码器的内部身份检查。

8.7.0 的客户端会把结构符合条件的 body 送进 extractBody;其中 multipart 分支却没有认出外来对象,留下没有产出和关闭路径的流。于是调用者等待的不是正常的完整上传。诊断事件 undici:client:sendHeaders 也不能证明请求字节已经发送到对端,它包含客户端内部处理阶段的信息。

不要把所有 AbortError 都归到这里。先保留取消原因、安装版本、body 的构造位置和实际 dispatcher。连接尚未建立时的地址族尝试超时,以及正文已经被提前读取,都是其他分支。

让请求、FormData 与 dispatcher 配套

对于由应用直接控制的独立 Undici 请求,可从同一个包入口导出 request、FormData 和 Agent。下面是受控上传片段,url 沿用应用已有测试目标,并保留业务所需的认证与状态处理:

const { request, FormData, Agent } = require('undici');

const dispatcher = new Agent();
try {
  const form = new FormData();
  form.append('name', 'upload-check');

  const response = await request(url, {
    method: 'POST',
    body: form,
    dispatcher,
    signal: AbortSignal.timeout(10_000),
  });
  console.log(response.statusCode);
  await response.body.dump();
} finally {
  await dispatcher.close();
}

这是异步函数内部的 CommonJS 片段。10 秒只是示例截止时间,应沿用应用的上传时限。长期服务通常复用其管理的 Agent,不必每次上传新建;这里显式配套,便于排除进程全局 dispatcher 被其他依赖更换的影响。

不要自行填写不带正确 boundary 的 multipart Content-Type;编码器应生成与正文一致的头。文件上传还应保留原 Blob、文件名与内容类型契约,并在目标端检查实际收到的内容,不能因改成一个字符串字段就认定原文件路径已验收。

若调用藏在第三方 SDK 内,应用新建一个 FormData 不一定能改变 SDK 的 body 或 dispatcher。应检查真实依赖路径,使用 SDK 支持的配置或兼容版本组合,不去覆盖进程全局对象或手改 Undici 私有字段。

当前 8.12.3 源码仍有结构判断与 FormData 身份判断的区别,但流的实现已经变化,不能把 8.7.0 的无限等待结论原样延伸到所有新版本。更新后仍需在实际包组合下验证,不能用“升到最新”代替兼容性判断。

验收包括服务端完整收到 multipart、字段与文件内容正确、取消正常终止、响应正文被消费或释放。超时后先核实业务是否已接收,特别是附件或创建接口;不要让自动重试反复提交同一份有副作用的上传。