[BUG] "Connection closed mid-response" (error=server_error) terminates Agent-tool (Task) subagents mid-turn with no in-agent recovery — macOS, 2.1.201/2.1.202
Preflight
- I searched existing issues. This is related to #70017 (SSE-heartbeat enhancement), #69415 and #69336 (general "connection closed mid-response" reports), but I am filing a distinct, transcript-forensics report that isolates the Agent-tool / subagent failure mode with concrete JSONL evidence and a reproducible counting method — data that isn't in those threads.
- Single bug report.
- Reproduced on the latest versions (2.1.201 and 2.1.202).
Summary
During a multi-agent orchestration run (a parent that fans out parallel Task subagents), API Error: Connection closed mid-response. The response above may be incomplete. fires repeatedly. On the subagent path there is no client-side recovery: the subagent's JSONL transcript ends exactly at the error record, so the subagent dies mid-turn with nothing after it. The foreground/main thread, by contrast, recovers and continues in the same session.
Environment
- OS: macOS (darwin)
- Claude Code: 2.1.201 and 2.1.202 (both stamped on affected records via the record
versionfield) - Entry point:
cli - Network: direct connection, no corporate proxy/VPN
- Workload: an autonomous multi-agent orchestrator — a parent dispatches many long-running
Tasksubagents, each running a plan → implement pipeline (long, investigation-heavy turns)
What I observed (quantified from the JSONL transcripts)
Ground-truthed via isApiErrorMessage: true — a raw grep of the banner text over-counts massively, because transcripts also quote the phrase. 11 genuine events in a single ~4.5 h session on 2026-07-07:
| timestamp (UTC) | context | error record position | records after error |
|---|---|---|---|
| 09:13:14 | subagent | line 101 / 101 | 0 |
| 09:40:21 | subagent | line 55 / 55 | 0 |
| 09:53:10 | subagent | line 42 / 42 | 0 |
| 10:18:58 | subagent | line 245 / 245 | 0 |
| 10:42:53 | subagent | line 154 / 154 | 0 |
| 10:58:57 | subagent | line 102 / 102 | 0 |
| 10:58:57 | subagent | line 108 / 145 | 37 |
| 11:41:07 | subagent | line 91 / 91 | 0 |
| 12:25:26 | subagent | line 53 / 53 | 0 |
| 13:07:38 | subagent | line 143 / 143 | 0 |
| 13:33:33 | main | line 298 / 316 | 18 |
9 of 10 subagent drops are the LAST record of the subagent transcript (error at line N of N) → the subagent terminated at the drop, nothing after. The single main-thread drop had 18 records after it → the foreground path recovered and kept going. This asymmetry matches #70017's note that background/Agent-tool dispatches get no stale-connection retry.
Example JSONL — the genuine error record (subagent)
Fields projected; cwd / sessionId / gitBranch omitted for privacy:
{"type":"assistant","isSidechain":true,"agentId":"<subtask-worker-id>","entrypoint":"cli","version":"2.1.202","error":"server_error","isApiErrorMessage":true,"uuid":"3741a4ea-…","parentUuid":"3d99c351-…","timestamp":"2026-07-07T10:18:58.592Z","message":{"role":"assistant","content":[{"type":"text","text":"API Error: Connection closed mid-response. The response above may be incomplete."}]}}
Two things worth highlighting on this record:
"error":"server_error"— the CLI itself tags these as a server-side close, not a local network fault."isSidechain":true(it is a subagent) and this record is line 245 of 245 in that subagent's transcript — nothing follows it.
How to reproduce the count on your own transcripts
# genuine events only (excludes quoted mentions of the banner text)
for f in ~/.claude/projects/**/*.jsonl; do
jq -c 'select(.isApiErrorMessage==true)
| {t:.timestamp, subagent:.isSidechain, err:.error, v:.version}' "$f"
done
# does a given subagent DIE at the drop? -> error record is the LAST line:
# (line number of the isApiErrorMessage:true record) == (wc -l of the file) => 0 records after
Additional finding — recovery cost
Because the subagent dies, recovery depends entirely on the parent orchestrator re-dispatching. A naive re-dispatch that re-runs the same large turn drops again at the same Connection closed mid-response; only a resume from a checkpoint (a smaller remaining turn) got through. So orchestrator-level auto-resume is a partial mitigation that still burns extra full dispatches. Correlated trigger: a large single-message emit (a ~9 KB file Write) following a long, silent-reasoning span under accumulated context — consistent with the "idle SSE stream → dropped mid-response" mechanism in #70017.
Expected behavior
Either (ideally both):
- a server-side SSE heartbeat during long reasoning spans, so the stream never idles into a mid-response close (see #70017); and/or
- a client-settable retry/resume for background (Agent-tool) dispatches, equivalent to the foreground stale-connection retry — so a dropped subagent resumes transparently instead of dying mid-turn.
Related issues
#70017 · #69415 · #69336 · #25979 · #67091