denoland/deno · error · TypeError

fetch failed

Error message

fetch failed

What it means

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).

Source

Thrown at ext/fetch/26_fetch.js:595

    if (cancelHandleRid !== null) {
      core.tryClose(cancelHandleRid);
    }
  }
  // Re-throw any body errors
  if (resp.error !== null) {
    if (inspectorRequestId !== null) {
      safeEmit(inspectorNetwork.loadingFailed, {
        requestId: inspectorRequestId,
        timestamp: DateNow() / 1000,
        type: "Fetch",
        errorText: resp.error[0],
      });
    }
    // `resp.error` is `[detail, cause]`, where `detail` is the full reqwest
    // message and `cause` is the underlying transport error detail. Mirror
    // Node's shape: `TypeError: "fetch failed"` with the detail in `.cause`.
    const cause = resp.error[1];
    throw new TypeError("fetch failed", { cause: new Error(cause) });
  }
  if (terminator.aborted) {
    // op_fetch_send resolved successfully, so the FetchResponseResource is already in
    // the resource table. The success path below either closes resp.responseRid
    // (redirect / null-body / HEAD / CONNECT) or hands it to createResponseBodyStream,
    // which owns its lifecycle. Only this aborted-after-resolve branch needs to close
    // the rid manually, otherwise it leaks and trips the test sanitizer.
    core.tryClose(resp.responseRid);
    return abortedNetworkError();
  }

  processUrlList(req.urlList, req.urlListProcessed);

  /** @type {InnerResponse} */
  const response = {
    headerList: resp.headers,
    status: resp.status,
    body: null,

View on GitHub (pinned to 89f33cbef2)

Solutions

  1. Inspect err.cause.message for the actual transport error before changing anything
  2. Verify the exact host/port is reachable: curl the same URL from the same machine
  3. Fix the environment: start the target server, correct DNS/hosts, or configure proxy variables
  4. For self-signed/custom CAs pass --cert or set DENO_CERT; avoid blanket --unsafely-ignore-certificate-errors in production
  5. Add retry with exponential backoff for genuinely transient failures (ECONNRESET, timeouts)

Example fix

// before
const res = await fetch("https://api.example.com/data"); // TypeError: fetch failed

// after
try {
  const res = await fetch("https://api.example.com/data");
} catch (err) {
  console.error("transport error:", err.cause?.message);
}
Defensive patterns

Strategy: retry

Validate before calling

async function reachable(url, timeoutMs = 3000) {
  const ctl = new AbortController();
  const t = setTimeout(() => ctl.abort(), timeoutMs);
  try {
    await fetch(url, { signal: ctl.signal });
    return true;
  } catch {
    return false;
  } finally {
    clearTimeout(t);
  }
}

Type guard

function isFetchFailed(err: unknown): err is TypeError {
  return err instanceof TypeError && err.message === "fetch failed";
}

Try / catch

async function fetchWithRetry(url, init, attempts = 3) {
  for (let i = 0; i < attempts; i++) {
    try {
      return await fetch(url, init);
    } catch (err) {
      const transient = err instanceof TypeError && err.message === "fetch failed" &&
        /timed out|connection reset|incomplete/i.test(String(err.cause?.message ?? ""));
      if (!transient || i === attempts - 1) {
        throw new Error(`fetch failed: ${err.cause?.message ?? err.message}`, { cause: err });
      }
      await new Promise((r) => setTimeout(r, 2 ** i * 250));
    }
  }
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of denoland/deno@89f33cbef2 (2026-08-16). Data as JSON: /api/errors/01b00d8d8b5a1b1f. Report an issue: GitHub.