[Bug] Streaming through local plaintext HTTP proxies broken on Windows native builds 2.1.221-2.1.223 (chunked responses aborted/rejected by embedded Bun runtime)
Summary
On Windows native builds (2.1.221, 2.1.222, 2.1.223), Claude Code cannot consume streaming ("stream": true) responses when ANTHROPIC_BASE_URL points to a local plaintext HTTP/1.1 proxy that returns Transfer-Encoding: chunked. The client aborts the connection 1–3 s after the first response bytes (proxy observes WSAECONNABORTED 10053 / WSAECONNRESET 10054), or rejects the response even after the proxy has delivered the complete, well-framed chunked body (message_stop + terminating 0\r\n\r\n).
After ~10 failed streaming retries the client silently falls back to a non-streaming request, which succeeds — so claude -p runs look like they "work" but only after minutes of retry storm, while interactive sessions surface API Error: Connection closed mid-response. The response above may be incomplete.
Environment
- Claude Code native win32-x64: 2.1.221, 2.1.222, 2.1.223 — all affected. Embedded runtime reports
X-Stainless-Runtime: node,X-Stainless-Runtime-Version: v26.3.0(Bun 1.4.0 based native binary). - Windows 11.
- Local proxy: cc-switch (
http://127.0.0.1:15721) → Anthropic-compatible upstream (MiniMax/anthropicendpoint). Also reproduced with a minimal ~20-line Node.js HTTP passthrough proxy, so this is not proxy-vendor-specific. - A previous install (before upgrading to 2.1.222 on 2026-08-05) streamed through the same proxy without issues.
Controlled experiments — what works vs. what fails
| Client | Path | Result |
|---|---|---|
| claude.exe 2.1.221–2.1.223 | → local plaintext HTTP proxy (chunked SSE) | FAIL — every stream aborted/rejected |
| claude.exe 2.1.223 | → HTTPS origin directly (chunked SSE over TLS) | OK |
| claude.exe 2.1.223 | → same proxy, non-streaming request (Content-Length response) | OK |
| curl / Node 22 fetch / Bun 1.3.12 fetch | → same proxy, same streaming request | OK (10/10) |
| claude.exe 2.1.223 | → local Node bridge that strips chunked framing (EOF-terminated body, Connection: close) | OK — first-try success every time |
The single variable that flips pass/fail in the A/B: whether the local server's response uses chunked framing. Same server software (Node http), same upstream, same request body — chunked passthrough fails, EOF-terminated succeeds.
Evidence
- Proxy-side byte capture of failing streams shows well-formed chunked SSE (
2B5\r\n…\r\n0\r\n\r\n) and ordinary response headers (content-type: text/event-stream; charset=utf-8,transfer-encoding: chunked, noContent-Lengthconflict, noConnectionweirdness). - Captured streams that were delivered in full (including
message_stopand the terminal zero chunk) were still followed by client retries — i.e. the client rejected even complete responses. - Abort timing is consistently 1–3 s after the first response chunk; non-chunked responses over the same connection path are always accepted.
Hypothesis
The embedded Bun runtime (1.4.0) mishandles plaintext HTTP/1.1 chunked responses — aborting mid-stream and/or failing to accept end-of-body — while the TLS code path and the Content-Length / close-delimited paths work correctly. Bun 1.3.12's fetch handles the same stream fine, so this looks like a regression in the embedded runtime rather than in CLI-level logic.
Minimal repro
- Run a local HTTP proxy that forwards
POST /v1/messagesto any Anthropic-compatible streaming endpoint and preservesTransfer-Encoding: chunked(a ~20-line Nodehttppassthrough is enough). ANTHROPIC_BASE_URL=http://127.0.0.1:<port> claude -p "say hi in one word"- Observe a multi-minute retry storm; the proxy logs show the client aborting each streamed response 1–3 s in, then a final non-streaming fallback request succeeding.
Workaround (for other affected users)
Put a tiny local bridge between Claude Code and the proxy that de-chunks responses: strip the transfer-encoding header and deliver the body EOF-terminated (Connection: close, no Content-Length). Streaming then works reliably on first attempt while keeping the proxy (routing/usage stats/failover) in the path.