[BUG] ECONNRESET on streaming API calls: bundled Bun HTTP client fails while Node.js/curl succeed identically (Windows, VPN-independent, survives reinstall)
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 fails with ECONNRESET on streaming API calls (/v1/messages), specifically and only when going through Claude Code's own bundled Bun HTTP client. An independent Node.js http2 client on the same machine, at the same time, over the same network path, completes identical long streaming requests successfully. This points to a bug in Claude Code's bundled Bun HTTP/TLS client (possibly related to its custom mTLS/CA certificate agent setup), not to network, VPN, DNS, TLS, or MTU issues on the user side — all of which were individually ruled out (see Additional Information).
Related to #38977 (closed, macOS-only) — same root symptom and cause, but reproduced here on Windows, and the fix that worked for that reporter (full uninstall/reinstall) does not work here.
What Should Happen?
Streaming responses of any length should complete reliably without connection resets, matching the behavior of a plain Node.js HTTP/2 client hitting the same endpoint under identical network conditions.
Error Messages/Logs
[DEBUG] [API REQUEST] /v1/messages x-client-request-id=... source=repl_main_thread
[DEBUG] Stream started - received first chunk
[DEBUG] [API:timing] first byte after 1678ms
[WARN] Stream connection error (ECONNRESET) — retrying streaming (2/10)
...
[WARN] Stream connection error (ECONNRESET) — retrying streaming (9/10)
[ERROR] Error in API request: Request was aborted.
[DEBUG] [engine] turn ended in error: [ede_diagnostic] turn aborted (aborted_streaming) stop_reason=null
Runtime confirmation (present in every session):
[DEBUG] [win32-proc-times] bun:ffi loaded, using procStartFt
Steps to Reproduce
- Connect to a VPN (reproduced across Astrill VPN "WireGuard, StealthVPN TCP, StealthVPN UDP, OpenWeb" and Red Shield VPN "RedLink TLS Plus"; not VPN-provider- or protocol-specific).
- Run
claude --debugin cmd.exe or PowerShell on Windows 11. - Send a prompt that will produce a moderately long streamed response (e.g. "explain in detail how X works").
- Observe
Unable to connect to API (ECONNRESET) · Retrying in Ns · attempt N/10, usually exhausting all 10 attempts and aborting the turn.
Isolation test (minimal reproducible comparison):
- In parallel, run a standalone Node.js script using the built-in
http2module, sending a real authenticated streaming POST tohttps://api.anthropic.com/v1/messages(same endpoint/TLS/ALPN h2), on the same machine, same VPN connection, run seconds apart from a failingclaude --debugattempt. - Result: the Node.js script completes 3/3 long streams (14-16s duration, 66-75 SSE chunks) with status=200 and zero errors, while
claude --debugfails on the same prompt at the same moment over the same network path.
Claude Model
Sonnet (default)
Is this a regression?
I don't know
Last Working Version
not identified
Claude Code Version
2.1.222
Platform
Anthropic API
Operating System
Windows
Terminal/Shell
Windows Terminal
Additional Information
Ruled out before isolating this as a Claude Code / Bun bug:
- DNS, TLS/certificate validation
- VPN provider and protocol (5 protocols across 2 providers — same result on all)
- MTU/fragmentation — extensively tested via
ping -f -lboundary search; determined to be a local adapter-MTU artifact, not a real external limit. Manually lowering MTU (1446 → 1360 → 1280) had no effect on the bug. - Full uninstall + reboot + reinstall of Claude Code (this fixed the issue for the original #38977 reporter; does not fix it here)
- Disabling IPv6 on the network adapter
Setting NODE_DEBUG=net,tls,http2 before claude --debug produces no Node.js-internal networking debug output, consistent with Claude Code running on Bun rather than Node.js and Bun not implementing Node's internal debug-log machinery.
Showing cached comments. Read the full discussion on GitHub ↗
5 Comments
Adding a data point that isolates this to the win32 build specifically, plus binary evidence for where the regression landed.
Same-hardware platform isolation
Windows 11 25H2 (OS build 26200.8973), single machine, Claude Code installed both natively and inside WSL2:
| Runtime | Version | Result |
|---|---|---|
| Windows native | 2.1.223 | ❌
Unable to connect to API (ECONNRESET)|| WSL2 (Ubuntu), same machine | 2.1.223 | ✅ works |
This complements the Node.js-vs-Bun isolation in the original report. That test showed the bundled client fails where another client on the same network path succeeds; this one shows the same Claude Code version on the same hardware succeeds when it runs the Linux build instead of the win32 build. Together they point at the Windows socket path in the bundled runtime rather than anything about the network.
Note WSL2 also changes the network path (Hyper-V vSwitch NAT, different MTU), so it is not a single-variable test on its own — but combined with the version boundary below it narrows the search considerably.
Version boundary
What changed in 2.1.220 → 2.1.221
I pulled the platform binaries from npm and diffed them. The unpacked size jumps at exactly that boundary:
| version |
linux-x64unpacked | delta ||---|---:|---:|
| 2.1.219 | 275,004,986 | — |
| 2.1.220 | 275,013,178 | +8 KB |
| 2.1.221 | 288,706,130 | +13.7 MB |
| 2.1.222 | 289,467,986 | +0.7 MB |
| 2.1.223 | 290,729,554 | +1.3 MB |
win32-x64jumps the same way: 265,721,050 → 278,279,898.Extracting strings from both binaries and diffing, these symbols are absent in 2.1.220 and present in 2.1.221:
Counts of matching lines for existing networking markers also move:
ALPNProtocols34 → 39,keepAlive75 → 91,tlsStringToProtocolVersion3 → 7. Of the 24,046 strings unique to 2.1.221, 1,241 match Bun runtime internal syntax (@undefined,@makeErrorWithCode,NGHTTP2_*).The Bun banner string is
Bun/1.4.0in both builds, so this appears to be an unversioned runtime rebuild — which would explain why the changelog has nothing about it. 2.1.220's notes are just "Bug fixes and reliability improvements".This is probably not a CA/mTLS issue
The original report suspects the custom mTLS/CA agent setup. The certificate-handling code appears byte-identical across the boundary — counts of matching lines are unchanged for
NODE_EXTRA_CA_CERTS(31),rejectUnauthorized(57),use-system-ca(7),rootCertificates(4),SELF_SIGNED_CERT_IN_CHAIN(7),UNABLE_TO_VERIFY_LEAF_SIGNATURE(7), andca-certificates.crt(3). Whatever broke, the trust-store path does not look like it moved.Not a Winsock LSP issue (re: #84211)
Our machines run MaLion (Intercom), a Japanese IT asset-management / DLP agent that installs 24 Winsock LSP catalog entries (12 × 64-bit, 12 × 32-bit;
pmilsp.dllandMarFtpLsp.dll, hooking TCP/UDP/RAW). Given #84211 this looked like an obvious match at first.It is not the cause. Other machines in our fleet with the identical MaLion configuration run 2.1.222 without any problem. Flagging this explicitly so the LSP angle from #84211 does not pull the diagnosis in the wrong direction — the failure clearly occurs both with and without an LSP present.
For reference: what 2.1.222 touched
2.1.222 added
createClaudeAiProxyFetch,shouldBypassProxyWithCidr,getNoProxy,usedProxy, andparseProxyResponse, consistent with its changelog entry about the connectivity check now using the proxy-aware transport. That did not resolve the failure here, so if it fixed a subset of reports, this is a different sub-case.Workaround
Pinning to 2.1.220 works. For fleet deployment, managed settings with
requiredMaximumVersion: "2.1.220"holds the ceiling until this is fixed.Update: Windows native build fails, WSL (Linux) build succeeds — same product, same VPN, different OS network layer
Additional data point that narrows this down further. On the same machine, same VPN connection (Astrill, WireGuard protocol), I installed Claude Code inside WSL2 (Ubuntu) via the standard install script:
curl -fsSL https://claude.ai/install.sh | bash
Result: Claude Code 2.1.223 running inside WSL2 completes the same previously-failing long-response prompt cleanly, with zero
ECONNRESETerrors — same VPN, same physical machine, same moment in time, run right after the native Windows build (2.1.222) failed on the identical prompt with the usualECONNRESET · Retrying ... attempt N/10pattern.I also tried the Code tab in the Claude desktop app in Local mode (which the docs confirm "runs the same engine as the CLI") on the same machine/VPN — it failed the same way, surfaced as:
Server error
Something went wrong. Try again in a moment. If it persists, check https://status.claude.com/.
API Error: Connection closed mid-response. The response above may be incomplete.
— consistent with it sharing the same underlying networking code as the native Windows CLI.
This suggests the bug isn't in Bun as a runtime in general (WSL's Linux build is very likely also Bun-based, and it works fine), but specifically in how the native Windows build's networking layer handles this VPN's virtual adapter (Wintun-based) under long-lived HTTP/2 streaming — something the WSL2 virtualized network path (and, per the earlier isolation test in this thread, plain Node.js on native Windows) doesn't trip over.
Summary of what's confirmed to fail vs. succeed on the same machine/VPN/moment:
http2client on native Windows (isolation test posted above) — succeeds@masakixx huge thanks for this!
The version-boundary isolation and binary diff work here is fantastic, way beyond what I could dig up myself.
Confirming it works: pinned to 2.1.220 on the same Windows 11 + Astrill VPN setup that was failing 100% of the time on 2.1.221-2.1.223, with
DISABLE_AUTOUPDATER: "1"in settings.json to keep it from silently updating back.claude --debugon the same long-response prompt that reliably triggered ECONNRESET now completes cleanly.Really appreciate the depth here, especially ruling out the mTLS/CA and LSP angles so cleanly.
Note: Desktop app's Code tab bundles a separate engine, unaffected by CLI version pins
For anyone hitting the same failure in the desktop app's Code → Local mode: pinning the standalone CLI to 2.1.220 (per the workaround above) does not fix it there. Per the docs ("Desktop runs the same underlying engine... they share configuration [but not the binary]"), the desktop app bundles its own separate copy of the Code engine, versioned independently and updated as part of the desktop app release, not via
DISABLE_AUTOUPDATERorminimumVersioninsettings.json— those only govern the standalone CLI installation.So this bug likely needs to be fixed (or the fix backported) in a desktop app release separately from the CLI release, and probably worth verifying both once a fix ships.
In the meantime, Code → Cloud in the desktop app works fine as a workaround, since those sessions run on Anthropic's infrastructure rather than the local bundled engine.
@pkropotin thanks — and glad the 2.1.220 pin is holding for you. Two things, one of which is a correction to something you credited me for.
Correction: the LSP angle was NOT ruled out. I was wrong.
The mTLS/CA exclusion stands — that came from the binary diff (certificate-handling code is unchanged across the 2.1.220 → 2.1.221 boundary) and nothing since has contradicted it.
The LSP exclusion does not stand. I based it on other machines in our fleet appearing to run 2.1.222 fine with the same endpoint agent installed. Since then:
The Winsock catalog before and after, on the same machine:
| | third-party catalog entries |
|---|---|
| before — 2.1.221+ fails | 24 —
MaLion.IWatandMaLion.FtpLsp.IWat, 32-bit and 64-bit chains, hooking TCP/UDP/RAW throughpmilsp.dllandMarFtpLsp.dll|| after uninstall — 2.1.223 works | 0 — only Microsoft base providers from
mswsock.dllremain |Two caveats on that, so it isn't read as more than it is. Uninstalling the agent removes more than its LSP — services and whatever else it installs go with it — so this is not a pure LSP-only isolation; the catalog change is precise, the rest of the agent's footprint is not something I can rule out. And relevant to #84211: the uninstaller cleaned up properly and left no orphaned entries, so this is not the leftover-LSP state described there.
I have not yet established why some of our machines were unaffected — most likely the agent version, edition, or centrally-applied policy differs and my "identical configuration" assumption did not hold. I'll post what I find.
Apologies for the misdirection, particularly since the whole point of that section was to keep triage away from a wrong lead.
Putting your environment and ours together
This is more interesting than either report alone. Neither of us has what the other has:
| | Network-layer intermediary | LSP present? |
|---|---|---|
| @pkropotin | Astrill VPN, Wintun virtual adapter | No — ruled out |
| ours | MaLion Cloud endpoint agent | Yes — 24 catalog entries |
| #84211 | leftover LSP from a game accelerator | Yes |
Different products, no overlap, and all three show the same 2.1.220 → 2.1.221 boundary with the same symptom.
So the trigger is probably not "Winsock LSPs" and not "VPN virtual adapters" specifically, but the native Windows build's socket path being fragile against non-standard Windows network paths in general. Your framing in the comment above scoped it to the Wintun adapter; I think the evidence now supports a wider scope than that.
Two consequences for triage:
Your WSL2 result and mine are independent reproductions of the same platform isolation, from two unrelated environments — native Windows build fails, Linux build succeeds, same hardware, same moment. Combined with the version boundary, that points squarely at the win32 socket implementation introduced in 2.1.221.
Still unexplained
I don't want to overclaim in the opposite direction this time. "Windows network-layer intermediary" is a hypothesis that fits three environments; it is not established, and it does not explain why some machines with the same agent are unaffected. That gap is real and I'm still working on it.
On the desktop app
Thanks for flagging that the desktop app bundles its own engine — that matters for managed deployments, since it means
requiredMaximumVersion/minimumVersion/DISABLE_AUTOUPDATERonly constrain the standalone CLI. An organization can pin its CLI fleet to 2.1.220 and still have every desktop-app user broken with no version lever available.One thing to add to that: on our machines, removing the endpoint agent fixed the desktop app's Code and Cowork tabs too, not just the CLI. So the desktop app isn't out of reach after all — it just has to be addressed from the environment side rather than the version side. (Whether that trade is acceptable is another matter; for us it isn't, since the agent is a deliberate control.)
That also tightens the causal argument here: a single environmental change fixed two independently-versioned binaries on the same machine — the standalone CLI at 2.1.223 and the desktop app's separately-bundled engine. Both carry the post-2.1.221 transport, and both recovered together.
Unchanged from my earlier comment
The version boundary and binary evidence are unaffected by any of this: 2.1.220 works, 2.1.221 onward does not, and 2.1.221 is where the bundled HTTP/socket implementation was replaced (+13.7 MB, new socket/HTTP2 symbols, certificate code unchanged). What changed is my assessment of what that implementation is incompatible with, not where the regression landed.