SSE heartbeat (≤30s) during model reasoning to prevent 'Connection closed mid-response' / stream-watchdog stalls on long (esp. Agent-tool) turns
Summary
Long-running turns — especially Agent-tool subagent dispatches — frequently fail with:
API Error: Connection closed mid-response. The response above may be incomplete.Agent stalled: no progress for <N>s (stream watchdog did not recover)
These happen during long silent model-reasoning spans, when the SSE response stream emits no bytes for an extended period. An intermediary (NAT/proxy/VPN) drops the idle connection, or the server closes it, and there is no client-side recovery for background/Agent-tool dispatches.
Request
Emit a periodic SSE heartbeat / comment line (≤30s) during model reasoning so the byte stream never goes idle long enough to be evicted or closed. This is the only true prevention — client-side keepalive (TCP keepidle) and watchdog-timeout tuning only mitigate or detect; they cannot stop an idle stream from being dropped mid-reasoning.
Why existing mechanisms don't cover this
- The TCP keepalive path (e.g. lowering macOS
net.inet.tcp.keepidle) only addresses NAT idle-eviction, not server-side closes or H2-layer silence. CLAUDE_ASYNC_AGENT_STALL_TIMEOUT_MSis a kill threshold, not a recovery.- The stream-retry behavior (
tengu_streaming_stale_connection_retry) appears to apply only to the foreground streaming path — Agent-tool subagent dispatches get no retry path regardless of flag state. Multi-agent / orchestration workloads (where one parent fans out many long-running subagents) are therefore the most exposed and the least covered.
Impact
In an autonomous multi-agent orchestrator, a single mid-reasoning close kills a subagent that may have run for many minutes. Mitigations (frequent commits, micro-dispatching, auto-resume) reduce data loss but cannot prevent the close. A server-side heartbeat would eliminate the failure class for all consumers, foreground and background alike.
Alternative / complementary ask
If a heartbeat isn't feasible, expose a client-settable retry/resume for background (Agent-tool) dispatches — equivalent to the foreground stale-connection retry — so orchestrators can recover transparently.
Environment
- Observed on macOS (darwin), Claude Code CLI, both foreground turns and
Agent-tool subagent dispatches. - Reproduces most reliably on long agent turns with extended reasoning between tool calls.
Showing cached comments. Read the full discussion on GitHub ↗
5 Comments
Found 3 possible duplicate issues:
This issue will be automatically closed as a duplicate in 3 days.
🤖 Generated with Claude Code
Bumping this to prevent auto-close — the underlying gap remains unaddressed.
To clarify why this is distinct from the cited issues: the specific ask is a server-sent SSE heartbeat at ≤30s cadence specifically during extended model reasoning, not only between tokens or during tool execution. Long reasoning windows are the exact interval where the stream goes idle and the TCP connection is silently dropped by intermediaries.
The critical compounding factor: Agent-tool (subagent) dispatches receive no retry. They are foreground-only — a mid-reasoning connection drop kills the dispatch permanently with no recovery path. A reasoning-phase heartbeat is the only server-side fix that closes this failure class; client-side keepalive tuning is insufficient.
For reference, the three issues cited as "duplicates" when this was closed are all closed without resolution: #45224 (closed NOT_PLANNED), #37534 (closed DUPLICATE), and #47059 (closed NOT_PLANNED). The underlying problem therefore remains open.
This continues to cause real data loss in long agentic workflows. Appreciate any update on whether a reasoning-phase heartbeat is on the roadmap.
Still hitting this regularly on Claude Code with long-running agentic sessions (Opus 4.8, heavy multi-subagent orchestration). In one ~heavy session today we recorded 7 distinct "Connection closed mid-response" main-loop deaths, including one silent window of ~1h42m while a background subagent ran. The pattern is consistent with the client runtime not setting SO_KEEPALIVE on the SSE connection: during a long silent reasoning/tool-wait window (no bytes flowing), a NAT/CDN hop evicts the idle connection at ~60–350s and the stream is dropped.
Client-side mitigations (turn-death markers + auto-resume from persisted state) recover the work, but they don't prevent the interruption — the user still sees the error and has to nudge a resume. A server-sent SSE heartbeat/keepalive frame (≤30s cadence) during long reasoning/tool execution is the only thing that actually prevents the eviction. Would love to see this prioritized — it's the single highest-leverage fix for long-horizon agent reliability. (Related: #45224, #37534, #47059.)
Corroborating data point (Claude Code 2.1.202, macOS) — confirms this hits the Agent-tool path exactly as described.
In a single ~4.5 h multi-agent orchestration run (a parent fanning out parallel
Tasksubagents), I counted 11 genuineAPI Error: Connection closed mid-responseevents, ground-truthed viaisApiErrorMessage: truein the JSONL transcripts to exclude quoted/echoed mentions. They were spread across the run (09:13–13:33), not a single burst — so not one upstream incident, but a recurring failure on long turns.Two forensic details that back the "no client-side recovery for Agent-tool dispatches" point:
Connection closed mid-responseon the same big turn; only a subsequent resume from a checkpoint (a smaller remaining turn) got through. So orchestrator-level auto-resume is only a partial mitigation — it costs extra full dispatches and still repeats the drop unless the retry is made smaller.Trigger correlated with a large single-message emit (a ~9 KB file
Write) following a long, investigation-heavy (silent-reasoning) span under accumulated context — consistent with the "long silent reasoning span → idle SSE stream → dropped mid-response" mechanism in the summary.+1 for the server-side SSE heartbeat. The complementary ask — a client-settable retry/resume for background (Agent-tool) dispatches, equivalent to the foreground stale-connection retry — would fully cover the multi-agent case even where a heartbeat isn't available, and would remove the "re-dispatch drops again" waste above.
Keep-alive: still reproducing regularly as of late July. Long reasoning stretches with no SSE heartbeat continue to cause client-side connection closes on otherwise-healthy agents (multiple per session in our autonomous-orchestration workload; two more today — both agents survived server-side and completed after resume, confirming the drops are transport-level, not model-level). A ≤30s heartbeat during reasoning remains the only prevention that would work client-side. Happy to provide fresh traces if useful.