Skip to content

OAuth error: fetch failed

Nothing answered. fetch failed is what a Node HTTP client says when the request never produced a response at all — no status line, no body, no server opinion about anything. That rules out a rejected credential, an expired token, a wrong organization, a quota, and a vendor 5xx, because every one of those requires a response that arrived. The OAuth error: prefix names the operation that was underway, not the kind of failure.

This is a network failure wearing an auth label, and that mislabelling is expensive: people spend an hour rotating credentials that were never sent to anyone.

The message is a wrapper, and it is hiding the useful part

fetch failed is deliberately generic. The actual reason — a DNS lookup that returned nothing, a refused connection, a certificate that would not validate, a proxy that dropped the socket — sits one level below it as an underlying cause, and the CLI surfaces the wrapper.

Get the layer underneath before you theorise. Run Claude Code with --debug, which writes a per-session debug log to ~/.claude/debug/<session-id>.txt; on Windows ~ means %USERPROFILE%. A certificate error, a DNS failure and a connection refusal have completely different fixes and identical top-level text, so guessing between them is a coin flip you can simply avoid.

Why “but my browser opens the page fine” proves nothing

This is the single most common false signal on this error, and it is worth being precise about why.

The browser and the CLI do not share network configuration. A browser follows the operating system’s proxy settings, uses the OS certificate store, and has its own DNS behaviour. A Node CLI follows whatever its own environment tells it — proxy environment variables, if anything reads them at all — and ships with its own trusted-certificate bundle rather than deferring to the system’s.

So on a corporate laptop, both of these are routinely true at the same time: the authorization page loads perfectly in Chrome, and the CLI’s own call to the same infrastructure never leaves the machine. The browser succeeding is evidence about the browser.

The second false signal is subtler: Claude Code itself working. Normal API traffic and the sign-in flow do not go to the same hostname. A corporate allowlist that permits the API host can block the sign-in host, and the result is a tool that runs fine for everyone who is already logged in and refuses to onboard anyone new. If claude works for your colleagues and only fails at login for you, stop looking at your account.

Telling the causes apart

Existing sessions work; only /login fails. Host-level filtering. The sign-in host is not on your allowlist. Confirm by running the login from a network with no proxy — a phone hotspot — and watching it succeed.

Everything fails, including normal requests, and it fails instantly. Suspect certificates. Claude Code retries transient failures up to ten times with exponential backoff, but TLS certificate validation failures are reported on the first attempt, without retries. A failure that appears immediately rather than after a visible retry sequence is behaving like a certificate problem, not like a flaky link.

Your company decrypts TLS. The corporate root certificate is installed in the OS store, which is why browsers are happy, and Node does not read that store. Ask whether TLS inspection is in place; if the answer is yes, this is almost certainly your cause, and the fix is to make the CLI’s runtime trust that certificate authority rather than to disable verification.

It began after a VPN, a container image bump, or a new machine. Suspect the change before the vendor. Containers frequently carry a minimal certificate bundle and no proxy configuration; a base image update can silently change both.

It is intermittent and other network tools are also flaky. Ordinary connectivity trouble. The login is not special; it is just the thing you happened to be doing.

It started after installing a plugin, an MCP server, or a hook. Launch with claude --safe-mode, which starts with all plugins, MCP servers and hooks disabled. If the login then works, a customization is altering the environment; if it does not, you have eliminated a whole category cheaply.

Is retrying useful?

No. A path that does not exist does not start existing because you asked twice.

DNS resolution, certificate validation and proxy policy are deterministic decisions made from unchanged inputs. Re-running /login re-runs them and gets the same answer. There is no server involved to be busy, no retry-after to honor, and no documented base delay to copy, so any wait you insert is a number you invented.

The narrow exception is genuine link flapping — and you would know, because your other tools would be misbehaving at the same time. If retrying once produces the same failure, treat it as deterministic and start on the diagnosis above rather than on a third attempt.

Fix by scenario

  • Sign-in host blocked while the API host is allowed — get the flow’s hosts through your egress policy. Ask for the login flow specifically; “Claude Code already works here” is exactly the reason this request gets closed as a duplicate.
  • TLS inspection — install your organization’s root certificate in a place the CLI’s runtime trusts, rather than turning off certificate verification. Disabling verification turns a visible failure into a silent exposure of every credential the tool handles, on every network you ever join.
  • Proxy configured in the OS only — set the proxy in the environment the CLI runs in. The OS-level setting is doing nothing for it.
  • Container or CI runner — add the certificate bundle and proxy configuration to the image. Do not bake a credential into the image to dodge the login; a key in a layer is a key in everyone’s registry.
  • Nothing above matches — read the debug log for the underlying cause and fix that specific error, which will have a real name.

While debugging, do not paste credentials, callback URLs, or raw logs into a chat window, a web form, or a third-party diagnostic site. Debug output from an auth flow can contain values that are as sensitive as the key itself.

How to confirm it’s fixed

Complete a sign-in on the network that was failing, not on the hotspot you used to diagnose it. Then quit, relaunch, and send one real request, which proves the flow produced a credential that was stored and can be read back.

If your fix was a certificate or proxy change, do it twice with a fresh process each time. The second run is the one that tells you the configuration is loaded from your environment rather than surviving in a shell you exported into by hand.

The presence or absence of a number in the message decides which page you need. OAuth error: Request failed with status code 500 has a status code, which means the round trip worked and the diagnosis is the exact inverse of this page’s. A login that dies at a fixed deadline means the path exists but is too slow, which is a third thing again. And if your login succeeds and requests still fail, the problem is credential resolution, not transport — Missing API key · Run /login covers the case where nothing resolves at all. The AI coding error triage tool places a symptom on that map before you start changing settings.