[BUG] ECONNRESET on streaming API calls: bundled Bun HTTP client fails while Node.js/curl succeed identically (Windows, VPN-independent, survives reinstall)

Status Open
Reported on v2.1.222
Maintainer reply None cached
Activity 6 comments · opened Aug 5, 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 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

  1. 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).
  2. Run claude --debug in cmd.exe or PowerShell on Windows 11.
  3. Send a prompt that will produce a moderately long streamed response (e.g. "explain in detail how X works").
  4. 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):

  1. In parallel, run a standalone Node.js script using the built-in http2 module, sending a real authenticated streaming POST to https://api.anthropic.com/v1/messages (same endpoint/TLS/ALPN h2), on the same machine, same VPN connection, run seconds apart from a failing claude --debug attempt.
  2. 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 --debug fails 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 -l boundary 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.

View original on GitHub ↗

5 Comments

masakixx · 25 days ago

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

  • 2.1.220 — works
  • 2.1.221, 2.1.222, 2.1.223 — all fail identically

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-x64 unpacked | 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-x64 jumps 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:

createServerParserShim
advanceResponsePipeline
kPipelinedResponses
bunHTTP2SessionTeardownFrame
newSocketFd
detachSocketListenersForHandoff
kStopParsingOnCloseListener

Counts of matching lines for existing networking markers also move: ALPNProtocols 34 → 39, keepAlive 75 → 91, tlsStringToProtocolVersion 3 → 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.0 in 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), and ca-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.dll and MarFtpLsp.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, and parseProxyResponse, 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.

pkropotin · 24 days ago

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 ECONNRESET errors — 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 usual ECONNRESET · Retrying ... attempt N/10 pattern.

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:

  • ❌ Native Windows Claude Code CLI (cmd.exe, PowerShell) — fails
  • ❌ Claude desktop app, Code tab, Local mode (same engine as CLI) — fails
  • ✅ Claude Code inside WSL2 (Linux build) — succeeds
  • ✅ Standalone Node.js http2 client on native Windows (isolation test posted above) — succeeds
pkropotin · 24 days ago

@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 --debug on 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.

pkropotin · 24 days ago

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_AUTOUPDATER or minimumVersion in settings.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.

masakixx · 24 days ago

@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:

  • Multiple additional users in our organization hit the same failure.
  • Uninstalling the endpoint agent that provides the Winsock LSP (MaLion Cloud, Intercom Co., Ltd.) restored normal operation on the affected machines.

The Winsock catalog before and after, on the same machine:

| | third-party catalog entries |
|---|---|
| before — 2.1.221+ fails | 24MaLion.IWat and MaLion.FtpLsp.IWat, 32-bit and 64-bit chains, hooking TCP/UDP/RAW through pmilsp.dll and MarFtpLsp.dll |
| after uninstall — 2.1.223 works | 0 — only Microsoft base providers from mswsock.dll remain |

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:

  1. #84211 is likely this same bug, not a separate LSP curiosity.
  2. Reproduction does not require a VPN. Our machines fail without one. Anyone trying to reproduce should be able to do it with any Windows socket-layer intermediary, which is a much easier setup than a specific VPN product.

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_AUTOUPDATER only 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.

Showing cached comments. Read the full discussion on GitHub ↗