## TL;DR

"TypeError: fetch failed" is workerd's generic way of saying the network call blew up, and it hides the real reason. Wrap the fetch in try/catch, log the exact URL that failed, then work through the usual suspects: DNS, TLS, timeouts, and whether the target is actually reachable from the public internet.

```text
Uncaught TypeError: fetch failed
```

## Steps

1. Catch it and get the details. Wrap the outbound fetch in try/catch and log the URL plus the error cause:
```js
try {
  const res = await fetch(url, { signal: AbortSignal.timeout(10000) });
  return res;
} catch (err) {
  console.log("fetch failed for", url, err.cause || err);
  throw err;
}
```
Expected: the tail log now shows which URL failed and a cause hint instead of a bare TypeError.

2. Watch the live failure:
```sh
wrangler tail
```
Expected: log lines naming the failing URL each time the error reproduces.

3. Test the endpoint from outside the worker with any HTTP client and check the basics: DNS resolves, the TLS certificate is valid and not expired, and the server answers in a reasonable time.
Expected: you find the layer that fails (DNS, TLS handshake, or hung server) independent of the worker.

4. Check the two workerd-specific traps: the request must go to a publicly reachable address (private network addresses are not reachable from the edge), and the fetch must not reuse an already-consumed request body.
Expected: the URL is public and each fetch builds its own request object.

5. Redeploy and confirm the tail log is clean on the previously failing path.
Expected: no more uncaught fetch failures.

## Use this when
- A worker throws uncaught "TypeError: fetch failed"
- An outbound API call works from your laptop but fails from the worker
- The error gives no URL or reason in the log

## Not for this skill when
- The error names the subrequest limit or the CPU time limit; those are quota errors, not network failures
- The fetch succeeds but returns an unexpected status; that is an API problem
- The failure is on an inbound request to the worker, not an outbound fetch

## Variant phrasings
- fetch failed cloudflare worker TypeError
- workerd uncaught exception fetch failed
- cloudflare worker outbound request fails with TypeError

## Why it happens

Workerd runs fetches through its own network stack and collapses every failure mode (DNS miss, TLS error, refused connection, timeout, unreachable host) into one TypeError. Without a try/catch that logs the URL and cause, you are debugging blind, because the default uncaught message carries none of the context.

## Edge cases
- Self-signed or expired certificates on the origin fail the TLS check; the fix is on the origin, not in the worker.
- An AbortSignal timeout that is too aggressive looks exactly like a server failure; give slow origins a realistic budget.
- Retries without backoff can turn one slow origin into a subrequest-limit error on top of the fetch failure.
- IPv6-only origins and origins behind aggressive bot protection fail in ways that look like generic network errors; test from a neutral network.

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_Zl2prmwnuVmv8P0MYTh_Pg
