Troubleshooting /

TypeError: fetch failed

Node.js reports every network failure in fetch() with the same two words. The real reason is one level down, in error.cause.

In short: Node’s built-in fetch (undici) throws TypeError: fetch failed when no HTTP response arrives: the name did not resolve, the connection was refused or timed out, a proxy was needed, or the certificate was not trusted. Print error.cause, find its code below, and fix that layer. A reply with status 401 or 429 is a different problem.

Last updated

Check the host from NetOkay

The website check requests the URL from NetOkay’s Worker. If NetOkay gets an HTTP response, the host is up and the failure is in the network path or settings of the machine that runs your code.

Check the host from NetOkay →

1. Print the real error

The message is always the same; the cause carries the system error code. Log it where you call fetch:

try {
  const res = await fetch(url);
} catch (err) {
  console.error(err.cause ?? err);
}

When the error comes from a tool you do not control, such as an editor extension, a build step or an AI agent, repeat the request with the same Node binary in the same shell. Replace the URL with the one that fails:

node -e "fetch(process.argv[1]).then(r => console.log(r.status), e => console.error(e.cause ?? e))" https://api.example.com/

A status number means fetch worked from this shell, so compare it with the environment the tool runs in. An error object gives you the code to look up next.

2. Match the cause code

3. curl works, Node fetch fails: the proxy

curl reads HTTPS_PROXY and HTTP_PROXY. Node’s built-in fetch ignores them unless you opt in, so on a network that only allows traffic through a proxy, curl succeeds and fetch times out. Node 24.5 and later, and 22.21 and later, honour the variables when NODE_USE_ENV_PROXY=1 is set or Node starts with --use-env-proxy:

NODE_USE_ENV_PROXY=1 HTTPS_PROXY=http://proxy.internal:3128 node app.js

On older versions, install undici and set a proxy-aware dispatcher once at start-up:

import { EnvHttpProxyAgent, setGlobalDispatcher } from 'undici';

setGlobalDispatcher(new EnvHttpProxyAgent());

Put internal hosts in NO_PROXY so they do not go through the proxy. Use only a proxy your network requires and you are allowed to use. For more on why a working curl does not prove your application’s path, read browser or curl works, your app fails.

4. Certificate errors behind a company proxy

Node trusts its own bundled list of root certificates, not the one your operating system or browser uses. Give it your organisation’s root certificate in PEM format:

NODE_EXTRA_CA_CERTS=/path/to/company-root.pem node app.js

Node reads NODE_EXTRA_CA_CERTS only when the process starts, so setting it from inside the application has no effect. On Node 23.8 and later, node --use-system-ca trusts the operating system’s store instead; Node 24.6 and later, and 22.19 and later, also accept NODE_USE_SYSTEM_CA=1.

Do not set NODE_TLS_REJECT_UNAUTHORIZED=0. It turns off certificate checks for every connection the process makes, not just the failing one.

5. Requests that take longer than the defaults

undici gives up after 10 seconds without a connection, and after 300 seconds without response headers or between body chunks. For a slow upstream you trust, pass a dispatcher with longer limits:

import { Agent } from 'undici';

const slow = new Agent({
  connect: { timeout: 30_000 },
  headersTimeout: 600_000,
  bodyTimeout: 600_000,
});
const res = await fetch(url, { dispatcher: slow });

A deadline you set yourself with AbortSignal.timeout() fails differently: it throws a TimeoutError, not fetch failed.

6. Messages that look similar but are not this error

7. Record what the failing environment sees

The NetOkay CLI runs on Node in the same shell and records which stage of a request to a public URL fails (DNS, connection, TLS trust or HTTP), and whether proxy variables and NODE_EXTRA_CA_CERTS are set there. It uses its own HTTPS client, so it shows the state of that environment rather than replaying your fetch call. See CLI: HTTP target.

Calling an AI provider? The AI API status pages show whether its API host answered NetOkay’s probes from several regions in the last few minutes.