[Bug] Async agent stall watchdog kills healthy long-running requests at 600s timeout

Status Open
Reported on v2.1.224
Maintainer reply None cached
Activity 2 comments · opened Aug 9, 2026

Bug Description
Background subagents are killed at exactly 600s by CLAUDE_ASYNC_AGENT_STALL_TIMEOUT_MS when the model's time-to-first-chunk exceeds it — the work is still alive, and the task is reported "failed".

What happens

The async-agent stall watchdog (CLAUDE_ASYNC_AGENT_STALL_TIMEOUT_MS, default 600000) aborts a background subagent after 600s with no stream chunk. It resets on every chunk and a ~1 Hz progress tick, and defers while a tool is in flight — so it can only fire in the window between a tool_result and the next assistant chunk. On expiry it aborts, writes [Request interrupted by user] into the subagent transcript, and reports the task failed with Agent stalled: no progress for 600s (stream watchdog did not recover).

The problem is that 600s is inside the normal latency distribution for heavyweight reasoning tiers at xhigh effort over 100k+ token contexts. It is not detecting a dead stream; it is guillotining a slow-but-healthy one.

Evidence (claude-code 2.1.224, macOS, 2026-08-09)

Measured across one machine's entire subagent transcript history:

| model | agents | stalls | rate |
|---|---:|---:|---|
| fable | 49 | 5 | 10.2% |
| opus | 158 | 4 | 2.5% |
| sonnet | 114 | 0 | 0% |
| haiku | 4 | 0 | 0% |

  • Every stall is at exactly 600.0s after a tool_result, matching the default.
  • Recovered near-misses at 560s, 475s and 407s — the latency tail runs right up to the wall, so the cutoff is arbitrary rather than diagnostic.
  • Stalls cluster across independent sessions: two different sessions stalled 70 seconds apart on 2026-08-03, with overlapping 600s windows. That is a shared upstream condition, not a per-agent fault.
  • Resuming the killed task by id completes it normally — proving the work was viable. (The resumed turn shows cache_read=0, so the whole context is re-paid.)

Impact

Long planning/analysis subagents on the top tiers fail ~10% of the time and lose all their work unless the user happens to notice and resume. Two of five agents in one session were only recovered manually; three were lost outright.

Suggested fixes

  1. Scale the default by model tier / effort level, or make it adaptive rather than a flat 600s.
  2. Auto-resume once on watchdog expiry before declaring the task failed — resume already works and is the documented recovery.
  3. Surface retry/backoff activity as a progress tick so the timer isn't counting during a live retry.
  4. Report it as a timeout rather than failed, and say the task is resumable in the notification itself.

Separate, smaller diagnostic issue

A subagent's .output path is a symlink into <project>/<session>/subagents/agent-<id>.jsonl. ls/stat report the link's size — the target path's length — so every subagent appears frozen at an identical byte count (146 on this machine) no matter how much it writes. This makes the symlink size look like a wedge signature and cost real debugging time; a healthy sonnet agent measures 146 too. Writing the file directly, or documenting that the path is a symlink, would avoid it.

Environment Info

  • Platform: darwin
  • Terminal: tmux
  • Version: 2.1.224
  • Feedback ID: 533358a4-89eb-4909-82d6-ce52f77fce95

Errors

[]

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗