{"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":595,"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":577,"sourceCodeEnd":613,"githubUrl":"https://github.com/denoland/deno/blob/89f33cbef296a2b287f323d42de54c871fa69c77/ext/fetch/26_fetch.js#L577-L613","documentation":"The generic TypeError raised when the underlying Rust HTTP transport (reqwest) fails before or while establishing communication: DNS resolution errors, refused connections, TLS handshake failures, or broken pipes. It mirrors Node's undici shape: the TypeError itself says only 'fetch failed' and the real transport detail is nested in error.cause (an Error built from the Rust error string).","triggerScenarios":"Nonexistent hostname (dns error), server not listening / wrong port (connection refused), invalid or self-signed TLS certificate, network unreachable, proxy misconfiguration (bad HTTPS_PROXY/http_proxy env), or connecting through to a port blocked by policy.","commonSituations":"CI runners without network access; corporate proxies that intercept TLS; localhost vs 127.0.0.1 mismatch when only IPv6 resolves; dev servers not started; expired DNS; self-signed certs in staging environments.","solutions":["Inspect err.cause.message for the actual transport error before changing anything","Verify the exact host/port is reachable: curl the same URL from the same machine","Fix the environment: start the target server, correct DNS/hosts, or configure proxy variables","For self-signed/custom CAs pass --cert or set DENO_CERT; avoid blanket --unsafely-ignore-certificate-errors in production","Add retry with exponential backoff for genuinely transient failures (ECONNRESET, timeouts)"],"exampleFix":"// before\nconst res = await fetch(\"https://api.example.com/data\"); // TypeError: fetch failed\n\n// after\ntry {\n  const res = await fetch(\"https://api.example.com/data\");\n} catch (err) {\n  console.error(\"transport error:\", err.cause?.message);\n}","handlingStrategy":"retry","validationCode":"async function reachable(url, timeoutMs = 3000) {\n  const ctl = new AbortController();\n  const t = setTimeout(() => ctl.abort(), timeoutMs);\n  try {\n    await fetch(url, { signal: ctl.signal });\n    return true;\n  } catch {\n    return false;\n  } finally {\n    clearTimeout(t);\n  }\n}","typeGuard":"function isFetchFailed(err: unknown): err is TypeError {\n  return err instanceof TypeError && err.message === \"fetch failed\";\n}","tryCatchPattern":"async function fetchWithRetry(url, init, attempts = 3) {\n  for (let i = 0; i < attempts; i++) {\n    try {\n      return await fetch(url, init);\n    } catch (err) {\n      const transient = err instanceof TypeError && err.message === \"fetch failed\" &&\n        /timed out|connection reset|incomplete/i.test(String(err.cause?.message ?? \"\"));\n      if (!transient || i === attempts - 1) {\n        throw new Error(`fetch failed: ${err.cause?.message ?? err.message}`, { cause: err });\n      }\n      await new Promise((r) => setTimeout(r, 2 ** i * 250));\n    }\n  }\n}","preventionTips":["Always log err.cause, not just the TypeError, to see the real transport error","Smoke-test target host/port with curl from the same environment before debugging code","Configure proxy env vars (HTTP_PROXY/HTTPS_PROXY/NO_PROXY) and CA certs (DENO_CERT) explicitly in CI","Use AbortController timeouts so hangs surface as aborts instead of silent stalls"],"tags":["network","fetch","dns","tls","proxy"],"backgroundTag":null,"analyzedSha":"89f33cbef296a2b287f323d42de54c871fa69c77","analyzedAt":"2026-08-16T07:54:21.310Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}