[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

Status Open
Reported on v2.1.235
Maintainer reply None cached
Activity 0 comments · opened Aug 19, 2026

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)

  1. Stream-stall-terminated turns fire the Stop hook again (previous behavior), so {"decision": "block"} recovery works; or
  2. StopFailure supports a decision field that resumes the turn; or
  3. 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

  1. Register the same hook command under Stop and StopFailure in settings.json; the script reads the transcript, checks whether the last conversation record has isApiErrorMessage: true with the stall text, logs hook_event_name, and prints {"decision": "block", "reason": "continue"}.
  2. On a connection that drops mid-response for >20s (or by interrupting connectivity mid-turn), trigger the "The response stopped arriving" termination.
  3. Observe: only StopFailure invocations 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

  1. Register the same hook command under Stop and StopFailure in settings.json; the script reads the transcript, checks whether the last conversation record has isApiErrorMessage: true with the stall text, logs hook_event_name, and prints {"decision": "block", "reason": "continue"}.
  2. On a connection that drops mid-response for >20s (or by interrupting connectivity mid-turn), trigger the "The response stopped arriving" termination.
  3. Observe: only StopFailure invocations 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_

View original on GitHub ↗