Mid-stream ECONNRESET on /v1/messages is fatal; SSE side-channel auto-reconnects, main stream does not

Resolved 💬 2 comments Opened May 21, 2026 by marcelofarias Closed May 25, 2026

Summary

The Anthropic API returned 200 OK on a streaming /v1/messages request, stalled for 15s with zero bytes, sent one chunk, then RST the TCP connection ~1s later. The error surfaces to the user as:

API Error: The socket connection was closed unexpectedly. For more information, pass verbose: true in the second argument to fetch()

Notably, the SSE side-channel at /v1/code/sessions/.../worker/events/stream dropped at the same time and auto-reconnected within ~1.2s. The main /v1/messages stream has no equivalent retry, so any mid-stream blip is fatal and bubbles all the way up to the user.

Recurring pattern — also caught on 2026-05-19 with the same signature (40 flow failures, 17 upstream RSTs to api.anthropic.com:443 captured via Network.NWError at the macOS network observer layer).

Environment

  • Claude Code: 2.1.146
  • macOS: 26.4
  • Shell: zsh
  • Node: v26.0.0
  • Running 6 parallel long-lived sessions in the same repo (via a claude-loop.sh wrapper that restarts on exit)

Forensic IDs (for server-log lookup)

| Field | Value |
|---|---|
| request-id | req_011CbFvtMSTLZ7dWV3KFiQ6k |
| x-client-request-id | d710bd12-4f52-4aeb-8b06-9bba36ff9cd8 |
| cf-ray | 9ff48517ce9402d0-CWB |
| timestamp (UTC) | 2026-05-21T15:12:55.017Z |

Timeline (from claude --debug --debug-file)

15:12:33.917  POST /v1/messages?beta=true → 200 OK (durationMs=2287)
15:12:48.914  [WARN] [Stall] stream_idle_partial lastChunkAgeMs=15000 bytesTotal=0 idleDeadlineMs=300000
15:12:53.980  Stream started — first chunk after ~20s of idle
15:12:55.017  [ERROR] code=ECONNRESET, message=The socket connection was closed unexpectedly
15:12:55.018  [ERROR] API error x-client-request-id=d710bd12-4f52-4aeb-8b06-9bba36ff9cd8
15:13:01.810  [ERROR] SSETransport: Stream read error (worker/events/stream) — same error class
15:13:01.811  [DEBUG] SSETransport: Reconnecting in 892ms (attempt 1)
15:13:03.016  [DEBUG] SSETransport: Connected   ← side-channel recovers
15:13:05.023  [WARN]  CCRClient: PUT worker failed: The operation timed out.

Two independent connections from the same client dropped within seconds of each other — points to a server-side / edge-network event rather than local network conditions.

Asks

  1. Server-side investigation. Please pull logs for the request IDs above. The stall-then-RST signature matches the 2026-05-19 incident, suggesting a recurring upstream issue (load balancer flap, edge restart, Cloudflare origin RST — exact failure mode opaque from the client side).
  1. Client-side: parity with SSETransport. Add retry-on-mid-stream-ECONNRESET to /v1/messages streaming. The SSE side-channel already does this cleanly (see SSETransport: Reconnecting in 892ms (attempt 1) above). The main message stream having no equivalent means every transient network blip is user-visible and breaks long-running sessions.

Local workaround (and why it's not enough)

I have a StopFailure hook (~/.claude/hooks/auto-retry-on-transient-error.sh) that on socket-closed errors plays a sound and sends a \"try again\" keystroke via AppleScript to the matching iTerm session, with a 3-retry-per-session_id cap.

In this incident, both hooks fired and exited 0:

15:12:55.025  StopFailure:unknown [afplay … Sosumi.aiff] completed with status 0
15:12:55.027  StopFailure:unknown [\$HOME/.claude/hooks/auto-retry-on-transient-error.sh] completed with status 0

…but the retry did not actually take effect end-to-end — the session remained in the error state rather than recovering. Possible causes are local (AppleScript-iTerm session matching, terminal focus state, the \"try again\" keystroke arriving when Claude isn't accepting input), but they illustrate why a userland workaround at this layer is inherently brittle. Native client-side retry (ask #2) would resolve this whole class of issue without the user-built scaffolding.

Reproduction

Not deterministic — happens organically when long-running streaming sessions encounter a transient network event. Seen across all 6 parallel sessions, roughly daily, with the same error signature.

claude: 2.1.146 (Claude Code)
macOS: 26.4

View original on GitHub ↗

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