[BUG] Subagent model-stall watchdog abort (~600s) surfaces as "[Request interrupted by user for tool use]" — 13 transcripts with a tight 600.0–605.6s gap signature
Preflight
Searched existing issues. Related but distinct — cross-linking rather than assuming same bug:
- #78915 — same exact string, but at dispatch time (subagent never runs). This report is about mid-run aborts of subagents that had been working normally for many turns. I've added our dispatch-time observations as a comment there.
- #70958 — sub-agent API 401 surfaced as the same string. Same "mis-surfacing sink", different trigger (auth vs. stall/timeout).
- #39259 — auto-mode safety classifier unavailability. Likely related to the dispatch-time variant (see my comment on #78915), not this mid-run one.
Environment
- Claude Code 2.1.206 (
claude --version), macOS (Darwin 25.x), desktop app; sessions frequently driven remotely from the iOS app — the operator was physically away from the Mac when these fired, so an accidental ESC/Ctrl-C at the keyboard is ruled out. - Subagents dispatched via the
Agent/Task tool with an explicitmodel: sonnetpin (all transcripts below ran onclaude-sonnet-5).
What's wrong
Long-running subagents die mid-run with the exact string [Request interrupted by user for tool use] recorded as a user-role message in the subagent transcript. The human operator did not interrupt (confirmed by the operator, repeatedly, across several days).
I analyzed every subagent transcript in one project directory (2026-08-02 → 2026-08-04) that contains the string — 14 files. 13 of 14 share a tight machine signature:
- The last record before the interrupt is a completed
tool_result— i.e. the subagent was waiting for its next model response when it died. - The gap between that record and the interrupt record clusters in 600.0–605.6 s (exactly 10 minutes + small overhead), across three different days:
| transcript (agent id) | date (UTC) | gap last-record → interrupt |
|---|---|---|
| agent-a91d591fdb2d295cf | 08-02 | 600.029 s |
| agent-ada1c1a2acfef1d02 | 08-02 | 600.067 s |
| agent-a2d68a55ee5d722bf | 08-02 | 602.197 s |
| agent-a12122965632ba5db | 08-03 | 605.018 s |
| agent-a0aae48c7ea3bff15 | 08-03 | 600.166 s |
| agent-a7666cd932335f354 | 08-03 | 603.224 s |
| agent-a0c98b11140c33af4 | 08-03 | 601.101 s |
| agent-a02f9c40996ff6f64 | 08-04 | 605.648 s |
| agent-a9fe407229917db5b | 08-04 | 604.508 s |
| agent-a0b3bd734e9fb5fb6 | 08-04 | 605.410 s |
| agent-ab4c1f66bf1bad6e2 | 08-04 | 604.312 s |
| agent-a95d42dc629f9b88d | 08-04 | 600.032 s |
| agent-a837c0a936a287d6e | 08-04 | 600.014 s |
(The 14th file has a 1476 s gap at line 6 — a different event, excluded.)
Human interrupts would produce arbitrary gaps; a cluster hugging exactly 600 s from below+overhead is a watchdog. Representative record sequence (agent-a837c0a936a287d6e):
2026-08-04T22:42:28.335Z assistant (claude-sonnet-5) thinking
2026-08-04T22:42:28.339Z assistant (claude-sonnet-5) tool_use
2026-08-04T22:42:28.390Z user tool_result
2026-08-04T22:52:28.404Z user text "[Request interrupted by user for tool use]" ← +600.014 s
Consistent with: the subagent's next model request (Sonnet) stalls during a model-availability incident, a ~600 s watchdog aborts it, and the abort is routed through the same path as a user interrupt, so it is recorded and surfaced as one.
Supporting evidence for the availability correlation: during the same incident windows, re-dispatching with the same model: sonnet pin tended to die the same way, while dropping the pin (inherit parent model) or doing the work in the parent succeeded. On non-incident days the identical pinned dispatches run fine.
What should happen
- A stalled/timed-out model request inside a subagent should surface as a model/timeout/availability error (like API errors do), not through the user-interrupt path. The parent agent — and the operator reading the transcript — currently misattribute the stoppage to a deliberate human interrupt, which derails the session ("did you cancel this?").
- Ideally the timeout would be retryable/named (e.g.
model request timed out after 600s), so the parent can decide to re-dispatch on a different model. - Side effect worth noting: subagents killed this way leak whatever they had allocated (in our case: spawned processes, port-lock directories, temp source clones), because they never reach their own cleanup step.
Steps to reproduce
Not deterministic — requires a model-availability incident:
- Dispatch a long-running subagent via the
Agenttool with an explicit model pin (model: sonnetin our cases). - Let it run for tens of minutes of normal tool use.
- If the pinned model degrades, the next model request stalls; ~600 s later the subagent transcript records
[Request interrupted by user for tool use]as a user message and the run is dead.
To check whether an occurrence matches this signature, measure the gap between the last record and the interrupt record in ~/.claude/projects/<project>/<sessionId>/subagents/agent-*.jsonl — machine timeout ≈ 600–606 s, human interrupts are arbitrary.