bigywater.tech
常见报错 · 33

Node.js TypeError: fetch failed:原因都在 e.cause 里,localhost 是 AggregateError,AbortSignal.timeout 是 TimeoutError

“TypeError: fetch failed” 是 Node 18 以后最让人摸不着头脑的报错,因为 message 里什么信息都没有。这篇在本机用小服务复现一系列失败(拒绝连接、断开、证书、超时、域名不存在),把每一种在 e.cause 里的样子列出来,再给一个按 cause.code 判断的重试写法。

4 个字
e.message 只有 “fetch failed”,原因全在 e.cause 里
AggregateError
请求 localhost 失败时 cause 的类型(IPv6 与 IPv4 各试一次),信息在 .errors 里
TimeoutError
AbortSignal.timeout 的结果,不是 TypeError,也没有 cause

只有 fetch failed

实测 · 本机执行输出
  node s1.js(退出码 1):
      typeof e = TypeError | e.message = "fetch failed" | e.code = undefined
      e.cause.constructor = Error | e.cause.message = connect ECONNREFUSED 127.0.0.1:<port>
      e.cause.code = ECONNREFUSED | errno = -61 | syscall = connect | address = 127.0.0.1 | port = <port>
      node:internal/process/promises:324
          triggerUncaughtException(err, true /* fromPromise */);
          ^
      [TypeError: fetch failed] {
        [cause]: Error: connect ECONNREFUSED 127.0.0.1:9770
            at TCPConnectWrap.afterConnect [as oncomplete] (node:net:2083:16) {
          errno: -61,
          code: 'ECONNREFUSED',
          syscall: 'connect',
          address: '127.0.0.1',
          port: 9770
        }
      }
      Node.js v26.12.0

外层永远是 TypeError: fetch failed,线索在 cause;日志里要把 cause 一起记下来。

localhost 的陷阱

实测 · 本机执行输出
  node s2.js:
      127.0.0.1 -> TypeError: fetch failed (0.02 s)
            cause: Error | connect ECONNREFUSED 127.0.0.1:<port> | code=ECONNREFUSED syscall=connect address=127.0.0.1
      localhost -> fetch failed | cause = AggregateError | code = ECONNREFUSED | message = ""
            errors[0]: connect ECONNREFUSED ::1:<port> | address = ::1
            errors[1]: connect ECONNREFUSED 127.0.0.1:<port> | address = 127.0.0.1

localhost 解析出多个地址,失败时 cause 是 AggregateError,要读 errors。

各种失败的样子

实测 · 本机执行输出
  node s3.js:
      服务端收到连接就关闭 -> TypeError: fetch failed (0.02 s)
            cause: Error | read ECONNRESET | code=ECONNRESET syscall=read
      服务端读到请求后正常关闭(FIN) -> TypeError: fetch failed (0.00 s)
            cause: SocketError | other side closed | code=UND_ERR_SOCKET
      服务端在请求后发 RST -> TypeError: fetch failed (0.00 s)
            cause: Error | read ECONNRESET | code=ECONNRESET syscall=read
      服务端回的不是 HTTP -> TypeError: fetch failed (0.00 s)
            cause: HTTPParserError | Response does not match the HTTP/1.1 protocol (Expected HTTP/, RTSP/ or ICE/) | code=undefined
      https 自签名证书 -> TypeError: fetch failed (0.00 s)
            cause: Error | self-signed certificate; if the root CA is installed locally, try running Node.js with --use-system-ca | code=DEPTH_ZERO_SELF_SIGNED_CERT
      https 访问 http 端口 -> TypeError: fetch failed (0.00 s)
            cause: Error | read ECONNRESET | code=ECONNRESET syscall=read
      域名不存在(.invalid) -> TypeError: fetch failed (0.01 s)
            cause: Error | getaddrinfo ENOTFOUND nonexistent-host.invalid | code=ENOTFOUND syscall=getaddrinfo
      URL 缺协议 -> TypeError: fetch failed (0.00 s)
            cause: Error | unknown scheme | code=undefined
      URL 不合法 -> TypeError: Failed to parse URL from not a url (0.00 s)
            cause: TypeError | Invalid URL | code=ERR_INVALID_URL

断开、重置、非 HTTP 响应、证书、DNS、协议缺失,各自的 cause 不同。

超时

实测 · 本机执行输出
  node s4.js:
      不设超时:3 秒后仍然挂着(等了 3.0 s)
      AbortSignal.timeout(500) -> TimeoutError: The operation was aborted due to timeout (0.50 s)
      手动 AbortController.abort() -> AbortError: This operation was aborted (0.30 s)
      TimeoutError 的 name = TimeoutError | instanceof TypeError = false | code = 23 | cause = undefined

不设超时会一直挂着;AbortSignal.timeout 得到 TimeoutError。

重试

实测 · 本机执行输出
  node s5.js:
      第 1 次失败:fetch failed | 原因 ECONNREFUSED
      第 2 次失败:fetch failed | 原因 ECONNREFUSED
      第 3 次失败:fetch failed | 原因 ECONNREFUSED
      成功:HTTP 200 hello(0.48 s)

按 cause.code(含 AggregateError)判断,不要按 message。

分步演示

请求失败
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64;报错文本都是真实复现时原样捕获的(命令行工具统一用 LC_ALL=C 取英文原文,路径换成 );所有服务都是本机上自己起的、只监听 127.0.0.1,不访问外网;计时用 time.perf_counter,数字每次运行会有小幅抖动。本篇用 Node v26.12.0 自带的全局 fetch;服务端都是脚本里起的 127.0.0.1 上的小服务;自签名证书用 openssl 现生成;ENOTFOUND 用保留域名 .invalid 触发,这一个查询会交给系统的 DNS 解析器,其余都不出本机
    • 没有验证:Node 18、20、22 上 cause 的具体文字;连接超时(UND_ERR_CONNECT_TIMEOUT)与 headersTimeout / bodyTimeout(需要 undici 的 Agent 选项,本篇没有安装 undici);代理环境下的失败;HTTP/2 下的失败;node-fetch、axios、got 等库的错误形态;重定向次数过多等 fetch 自己的错误
    京ICP备2026067502号-1京公网安备11010502063487号