{"record":{"id":"01b00d8d8b5a1b1f","repo":"denoland/deno","slug":"fetch-failed","errorCode":null,"errorMessage":"fetch failed","messagePattern":"fetch failed","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"ext/fetch/26_fetch.js","lineNumber":596,"sourceCode":"    if (cancelHandleRid !== null) {\n      core.tryClose(cancelHandleRid);\n    }\n  }\n  // Re-throw any body errors\n  if (resp.error !== null) {\n    if (inspectorRequestId !== null) {\n      safeEmit(inspectorNetwork.loadingFailed, {\n        requestId: inspectorRequestId,\n        timestamp: DateNow() / 1000,\n        type: \"Fetch\",\n        errorText: resp.error[0],\n      });\n    }\n    // `resp.error` is `[detail, cause]`, where `detail` is the full reqwest\n    // message and `cause` is the underlying transport error detail. Mirror\n    // Node's shape: `TypeError: \"fetch failed\"` with the detail in `.cause`.\n    const cause = resp.error[1];\n    throw new TypeError(\"fetch failed\", { cause: new Error(cause) });\n  }\n  if (terminator.aborted) {\n    // op_fetch_send resolved successfully, so the FetchResponseResource is already in\n    // the resource table. The success path below either closes resp.responseRid\n    // (redirect / null-body / HEAD / CONNECT) or hands it to createResponseBodyStream,\n    // which owns its lifecycle. Only this aborted-after-resolve branch needs to close\n    // the rid manually, otherwise it leaks and trips the test sanitizer.\n    core.tryClose(resp.responseRid);\n    return abortedNetworkError();\n  }\n\n  processUrlList(req.urlList, req.urlListProcessed);\n\n  /** @type {InnerResponse} */\n  const response = {\n    headerList: resp.headers,\n    status: resp.status,\n    body: null,","sourceCodeStart":578,"sourceCodeEnd":614,"githubUrl":"https://github.com/denoland/deno/blob/a961cdec3b1948844414ebeecc697dcad76df2d1/ext/fetch/26_fetch.js#L578-L614","documentation":"fetch() throws a bare TypeError 'fetch failed' for any transport-level failure: DNS resolution errors, refused connections, TLS handshake problems, proxy failures, or resets before a response was received. Following Node/undici's shape, the human-readable detail is not in the message but in error.cause, which carries the underlying reqwest transport error text.","triggerScenarios":"fetch('http://localhost:9999/') with nothing listening (connection refused); unresolvable hostname (DNS error); self-signed/expired/mismatched certificate (TLS error); connecting to an https server over plain http or vice versa; HTTP_PROXY/HTTPS_PROXY pointing at a dead proxy; blocked network path (firewall, dropped packets).","commonSituations":"Integration tests hitting a service that is not started or crashed; Docker/Kubernetes network policies blocking egress; corporate proxy env vars misconfigured; expired TLS certs in CI; DNS failing inside containers; IPv6-only endpoints unreachable from the host.","solutions":["Read err.cause.message first — it contains the actual transport reason (dns error, tcp connect error, invalid certificate, ...)","Connection refused: verify host/port and that the server listens on the right interface","TLS problems: fix the certificate chain or pass the CA with deno run --cert=ca.pem","Proxy problems: correct or unset HTTP_PROXY/HTTPS_PROXY/NO_PROXY for the target host","DNS problems: test with Deno.resolveDns or curl from the same environment","For flaky networks, add retry with backoff for idempotent (GET) requests"],"exampleFix":"// before\nconst res = await fetch(url); // TypeError: fetch failed\n\n// after\ntry {\n  const res = await fetch(url);\n} catch (err) {\n  if (err instanceof TypeError && err.message === \"fetch failed\") {\n    console.error(\"transport failure:\", err.cause?.message);\n  }\n  throw err;\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  const res = await fetch(url, init);\n} catch (err) {\n  if (err instanceof TypeError && err.message === \"fetch failed\") {\n    const detail = err.cause?.message ?? \"\";\n    if (/refused|timed out|reset/i.test(detail)) {\n      // transient: retry with backoff (idempotent requests only)\n    } else if (/dns/i.test(detail)) {\n      // configuration: bad hostname\n    } else if (/certificate|tls|ssl/i.test(detail)) {\n      // configuration: trust store / --cert\n    }\n  } else throw err;\n}","preventionTips":["Always log err.cause, not just 'fetch failed'","Health-check dependencies at startup instead of failing on first request","Retry only idempotent methods, with capped backoff and jitter","Verify proxy env vars and CA configuration in containers and CI"],"tags":["fetch","network","dns","tls","proxy","connection"],"backgroundTag":"fetch-network-error","analyzedSha":"a961cdec3b1948844414ebeecc697dcad76df2d1","analyzedAt":"2026-08-20T13:07:44.778Z","contentChangedAt":"2026-08-20T13:07:44.778Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}