[BUG] Stop hook no longer fires on stream-stall-terminated turns ("The response stopped arriving") — StopFailure fires instead but its decision output is ignored, breaking auto-continue hooks
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?
Environment
- Claude Code 2.1.235 (latest at time of writing)
- Windows 11 Pro (10.0.26100), VS Code extension and terminal CLI
- Unstable mobile network connection (frequent >20s stream idle periods)
Summary
When a turn is terminated by the client-side stream-idle watchdog, the error message recently changed from API Error: Response stalled mid-stream. The response above may be incomplete. to API Error: The response stopped arriving. The response above may be incomplete. Alongside the wording change, the hook path changed: these turns previously fired the Stop hook, whose {"decision": "block", "reason": "..."} output successfully auto-continued the session. They now fire only StopFailure, whose output is ignored by design. This silently broke hook-based auto-continue recovery for users on unstable connections.
Before (working, through ~mid-August 2026)
A Stop hook that detected the trailing isApiErrorMessage record in the transcript and printed {"decision": "block", "reason": "continue where you left off..."} reliably resumed the session. Our hook log shows dozens of successful auto-continues per day through 2026-08-18, e.g.:
2026-08-18 21:58:16 session 93f65178-…: stall detected (attempt 1), checking connectivity
2026-08-18 21:58:16 session 93f65178-…: online - issuing auto-continue (1/3)
After (broken, observed 2026-08-19 on 2.1.235)
The same script registered under both Stop and StopFailure, instrumented to log the hook_event_name of every invocation that sees a trailing API-error record:
- Stop never fires for turns terminated by "The response stopped arriving" — zero
[Stop]entries across dozens of stalls in multiple sessions. - StopFailure fires, the hook matches the error and prints the block decision — and the session still sits waiting for a manual "continue":
2026-08-19 15:20:52 session 44a1d6f1-…: [StopFailure] saw API error: 'API Error: The response stopped arriving. The response above may be incomplete.'
2026-08-19 15:20:52 session 44a1d6f1-…: [StopFailure] online - issuing auto-continue (1/3)
2026-08-19 15:25:22 session 44a1d6f1-…: [StopFailure] saw API error: 'API Error: The response stopped arriving. The response above may be incomplete.'
2026-08-19 15:25:22 session 44a1d6f1-…: [StopFailure] online - issuing auto-continue (2/3)
(Both timestamps correspond to the user manually typing "continue" in the UI.)
Impact
- Users on unstable connections must manually type "continue" many times per session; unattended/long-running sessions cannot finish.
- When the stall lands while a tool call is pending (e.g.
AskUserQuestion), the dropped stream is recorded as a tool rejection ("The user doesn't want to proceed with this tool use"), which derails the turn beyond a simple stall. - Background subagents terminate entirely on the same error ("Agent terminated early due to an API error") and require manual resume — in one session the same research subagent was killed three consecutive times.
Expected behavior (any of)
- Stream-stall-terminated turns fire the
Stophook again (previous behavior), so{"decision": "block"}recovery works; or StopFailuresupports a decision field that resumes the turn; or- Built-in retry/resume for stalls after partial output (with tool-call dedup safety), since these are currently never retried.
Related issues (symptom family — none cover the hook regression)
- #74672 — "Response stalled mid-stream" frequency report (macOS, 2.1.177)
- #77301 — same error killing advisor consultations (macOS, 2.1.207)
- #69415 — "Connection closed mid-response" makes long responses unusable (Windows/WSL2, 2.1.181; reporter argues client streaming-layer cause: same tasks succeed in other tools on the same machine/network)
Those issues report the stall itself. This issue is about the broken recovery path: the one supported self-help mechanism (a Stop hook auto-continue) stopped working when these terminations moved to the non-blockable StopFailure event. A community-observed mitigation in those threads — splitting large single outputs into many small tool calls so each response stream stays short — reduces exposure but doesn't restore recovery.
Reproduction
- Register the same hook command under
StopandStopFailureinsettings.json; the script reads the transcript, checks whether the last conversation record hasisApiErrorMessage: truewith the stall text, logshook_event_name, and prints{"decision": "block", "reason": "continue"}. - On a connection that drops mid-response for >20s (or by interrupting connectivity mid-turn), trigger the "The response stopped arriving" termination.
- Observe: only
StopFailureinvocations see the error; the printed decision has no effect; the session waits for manual input.
"hooks": {
"Stop": [{ "hooks": [{ "type": "command", "command": "python .../auto_continue_stall.py", "timeout": 660 }] }],
"StopFailure": [{ "hooks": [{ "type": "command", "command": "python .../auto_continue_stall.py", "timeout": 660 }] }]
}
What Should Happen?
When a turn is terminated by a stream stall ("The response stopped arriving"), hook-based recovery should work the way it did before ~August 2026. Any one of these would restore it:
Fire the Stop hook on stall-terminated turns (previous behavior), so a hook returning {"decision": "block", "reason": "..."} can auto-continue the session — this worked reliably for the older "Response stalled mid-stream" wording of the same error.
Make StopFailure blockable — honor a decision field so a recovery hook can resume the turn from the failure path.
Retry stalled streams natively, even after partial output — currently these are never retried by design (to avoid duplicate tool execution); a safe resume that deduplicates already-executed tool calls would remove the need for recovery hooks entirely.
At minimum, users on unstable connections should have some supported unattended-recovery path; today the only option is manually typing "continue" many times per session, and stalls that land mid-tool-call are additionally misrecorded as the user rejecting the tool.
Error Messages/Logs
Steps to Reproduce
- Register the same hook command under
StopandStopFailureinsettings.json; the script reads the transcript, checks whether the last conversation record hasisApiErrorMessage: truewith the stall text, logshook_event_name, and prints{"decision": "block", "reason": "continue"}. - On a connection that drops mid-response for >20s (or by interrupting connectivity mid-turn), trigger the "The response stopped arriving" termination.
- Observe: only
StopFailureinvocations see the error; the printed decision has no effect; the session waits for manual input.
"hooks": {
"Stop": [{ "hooks": [{ "type": "command", "command": "python .../auto_continue_stall.py", "timeout": 660 }] }],
"StopFailure": [{ "hooks": [{ "type": "command", "command": "python .../auto_continue_stall.py", "timeout": 660 }] }]
}
### Claude Model
Opus
### Is this a regression?
Yes, this worked in a previous version
### Last Working Version
_No response_
### Claude Code Version
2.1.223
### Platform
Anthropic API
### Operating System
Windows
### Terminal/Shell
VS Code integrated terminal
### Additional Information
_No response_