[BUG] Claude Code 2.1.220: startup connectivity preflight hard-fails behind a corporate HTTPS proxy (ERR_SOCKET_CLOSED), even though the API is reachable through it

Status Fixed / completed
Reported on v2.1.220
Maintainer reply None cached
Activity 3 comments · opened Jul 27, 2026 · closed Aug 19, 2026

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

Claude Code 2.1.220 refuses to start when it sits behind a corporate HTTPS proxy (an outbound proxy you reach over TLS, i.e. HTTPS_PROXY=https://user:pass@proxy.corp.example:8443 — the common setup in enterprise networks with authenticated egress / TLS‑inspecting proxies). On launch it hangs on the connectivity preflight and then dies:

✻ Checking connectivity...
Unable to connect to Anthropic services
Failed to connect to api.anthropic.com: ERR_SOCKET_CLOSED
Please check your internet connection and network settings.

The corporate proxy works and the Anthropic API is fully reachable through it — only the startup connectivity preflight fails. Because that preflight is a hard blocking gate, the entire CLI becomes unusable on a network where it could actually operate.

Why this is a bug (not a broken proxy)

Through the exact same corporate proxy (HTTPS_PROXY=https://user:***@proxy.corp.example:8443):

| Request | Result |
|---|---|
| curl -x "$HTTPS_PROXY" https://api.anthropic.com/api/hello | 200 |
| curl -x "$HTTPS_PROXY" https://platform.claude.com/v1/oauth/hello | 200 |
| Node global fetch (undici) → api.anthropic.com via the proxy | works (200/401) |
| ANTHROPIC_API_KEY=<invalid> claude -p hi (SDK path) via the proxy | reaches API → "Invalid API key" |
| Interactive startup connectivity preflight | ERR_SOCKET_CLOSED ❌ |

The SDK request path and curl both tunnel correctly through the corporate TLS proxy and get HTTP 200 from both /api/hello and /v1/oauth/hello. The preflight is the only thing that fails.

Root cause

The blocking preflight (dvm() in the bundle) does:

let r = [ `${BASE_API_URL}/api/hello`,           // https://api.anthropic.com/api/hello
          `${TOKEN_URL.origin}/v1/oauth/hello` ]; // https://platform.claude.com/v1/oauth/hello
let n = async (s) => {
  const a = await Mo.get(s, { headers: { "User-Agent": … } }); // NOT the Anthropic SDK client
  if (a.status !== 200) return { success:false, error:`… Status ${a.status}` };
  return { success:true };
  // catch → { success:false, error:`Failed to connect to ${host}: ${a.code}` } // ← ERR_SOCKET_CLOSED
};
await Promise.all(r.map(n)); // both must return 200, else startup is blocked (process.exit(1))

Two problems:

  1. The preflight uses a different HTTP client (Mo.get) than the main SDK. That client does not tunnel through an https:// (TLS‑to‑proxy) corporate proxy — it dies with ERR_SOCKET_CLOSED, while the SDK/undici path and curl succeed against the same URLs. This is the known TLS‑in‑TLS proxy limitation (see #71796).
  2. The preflight does not skip when a proxy is configured. The other connectivity probe in the same codebase (the latency HEAD /api/hello) explicitly bails when a proxy is set:

``js
if (Z.HTTPS_PROXY || Z.https_proxy || Z.HTTP_PROXY || Z.http_proxy || …) return;
`
The **blocking** preflight
dvm()` has no such guard, so it runs its proxy‑incompatible client and hard‑fails — precisely on the corporate‑proxy setups where the guard matters most.

Impact

Claude Code is completely unusable behind a corporate HTTPS proxy on 2.1.220, even though it can actually reach the Anthropic API through that proxy. There is no documented env var to skip the preflight. The only workaround is downgrading to 2.1.190, which has no such blocking preflight.

What Should Happen?

Claude Code should start normally behind a corporate HTTPS proxy whenever the Anthropic API is actually reachable through that proxy (which it is here — see the table below). Concretely, the startup connectivity preflight should do any of:

  • succeed, by issuing its probe requests through the same proxy‑aware client/dispatcher the SDK already uses (that path tunnels correctly through the TLS proxy); or
  • skip itself when a proxy is configured (the codebase already does this for the non‑blocking latency probe — see Root cause); or
  • degrade to a non‑blocking warning rather than a hard process.exit, and let the first real API request be the source of truth.

The CLI should then proceed to the login/prompt, exactly as it does on 2.1.190.

Error Messages/Logs

On-screen error at startup:


✻ Checking connectivity...

Unable to connect to Anthropic services

Failed to connect to api.anthropic.com: ERR_SOCKET_CLOSED

Please check your internet connection and network settings.

Note: Claude Code might not be available in your country. Check supported
countries at https://anthropic.com/supported-countries


`strace -f -e trace=connect` on the interactive startup shows the preflight **does reach the proxy** (it is not bypassing it and going direct) and then the request dies — it retries the two probe endpoints several times, each ending in `ERR_SOCKET_CLOSED`:


# all connect() calls go to the corporate proxy IP (not directly to api.anthropic.com):
connect(…, {sa_family=AF_INET, sin_port=htons(8443), sin_addr=inet_addr("<proxy-ip>")}, 16) = -1 EINPROGRESS
… (x6, i.e. ~3 retries per endpoint)
# 0 AF_INET6 connect() calls; 0 direct connects to api.anthropic.com


For contrast, the **same two probe URLs return 200 through the same proxy** with a proxy‑aware client:


$ curl -sS -o /dev/null -w '%{http_code}\n' -x "$HTTPS_PROXY" https://api.anthropic.com/api/hello
200
$ curl -sS -o /dev/null -w '%{http_code}\n' -x "$HTTPS_PROXY" https://platform.claude.com/v1/oauth/hello
200


And the SDK path reaches the API through the same proxy (auth error proves connectivity, not a network error):


$ ANTHROPIC_API_KEY=sk-ant-invalid claude -p hi
Invalid API key · Fix external API key

Steps to Reproduce

  1. Put Claude Code behind a corporate HTTPS proxy you reach over TLS:

export HTTPS_PROXY=https://user:pass@proxy.corp.example:8443 (and HTTP_PROXY to the same).

  1. Confirm the proxy itself works and the API is reachable:
  • curl -x "$HTTPS_PROXY" https://api.anthropic.com/api/hello200
  • curl -x "$HTTPS_PROXY" https://platform.claude.com/v1/oauth/hello200
  1. Run claude (interactive).
  2. Observe it stall on ✻ Checking connectivity..., then fail with

Unable to connect to Anthropic services / Failed to connect to api.anthropic.com: ERR_SOCKET_CLOSED,
even though step 2 proves the endpoints are reachable through the same proxy.

Claude Model

None

Is this a regression?

Yes, this worked in a previous version

Last Working Version

190

Claude Code Version

Claude Code 2.1.220 (npm global), Node.js v26, Linux x64

Platform

Anthropic API

Operating System

macOS

Terminal/Shell

Terminal.app (macOS)

Additional Information

Suggested fixes (any one)

  1. Route the preflight through the same SDK/dispatcher used for real API calls (it already handles corporate proxies correctly).
  2. Apply the same if (HTTPS_PROXY || HTTP_PROXY) return skip guard the latency probe already uses.
  3. Make preflight failure a non‑blocking warning instead of a hard process.exit — let the first real API call be the source of truth.
  4. Add an opt‑out env var, e.g. CLAUDE_CODE_SKIP_CONNECTIVITY_CHECK=1.
  • Workaround: downgrading to 2.1.190 resolves it — that version has no blocking startup preflight and goes straight to onboarding/login. Real API traffic works through the corporate proxy on 2.1.190 as well.
  • A local http:// bridge does not help. Putting a plain http://127.0.0.1:PORT proxy in front of the corporate TLS proxy (so the client only speaks plain HTTP locally) still fails the preflight, which points at the preflight's HTTP client rather than the transport alone.
  • The codebase already knows proxies are special. The non‑blocking latency probe (HEAD /api/hello) short‑circuits with if (HTTPS_PROXY || http_proxy || …) return;, but the blocking preflight dvm() has no equivalent guard — so the check that can hard‑stop the CLI is the one missing proxy handling.
  • No documented opt‑out. The env‑var docs list API_TIMEOUT_MS, API_FORCE_IDLE_TIMEOUT, ANTHROPIC_BASE_URL, etc., but nothing to skip or soften the startup connectivity check.
  • Likely related issues: #71796 (HTTPS proxy → ERR_SOCKET_CLOSED, TLS‑in‑TLS not supported by Node built‑in HTTP), #15615 ("Stuck in Checking connectivity"), #60133 (socket connection closed unexpectedly on proxies).
  • Proxy credentials/host redacted throughout; the proxy is a standard authenticated, TLS‑terminating corporate egress proxy.

View original on GitHub ↗

This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗