[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
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:
- The preflight uses a different HTTP client (
Mo.get) than the main SDK. That client does not tunnel through anhttps://(TLS‑to‑proxy) corporate proxy — it dies withERR_SOCKET_CLOSED, while the SDK/undici path andcurlsucceed against the same URLs. This is the known TLS‑in‑TLS proxy limitation (see #71796). - 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;
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.
The **blocking** preflight
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
- 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).
- Confirm the proxy itself works and the API is reachable:
curl -x "$HTTPS_PROXY" https://api.anthropic.com/api/hello→ 200curl -x "$HTTPS_PROXY" https://platform.claude.com/v1/oauth/hello→ 200
- Run
claude(interactive). - 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)
- Route the preflight through the same SDK/dispatcher used for real API calls (it already handles corporate proxies correctly).
- Apply the same
if (HTTPS_PROXY || HTTP_PROXY) returnskip guard the latency probe already uses. - Make preflight failure a non‑blocking warning instead of a hard
process.exit— let the first real API call be the source of truth. - 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 plainhttp://127.0.0.1:PORTproxy 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 withif (HTTPS_PROXY || http_proxy || …) return;, but the blocking preflightdvm()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.
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗