[BUG] Subagent burns 256k output tokens on thinking across four max_tokens turns, produces nothing, and the stall watchdog never fires
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report
- [x] I am using the latest version of Claude Code
What's Wrong?
A background subagent spent its entire output budget on reasoning across four separate turns, made four tool calls in 38 minutes, produced no file and no final text, and nothing surfaced to the parent session. No error, no warning, no notification. The stall watchdog never fired, because thinking tokens keep the stream alive.
Environment
- Claude Code 2.1.280, WSL2 Ubuntu on Windows 10
- Subagent model
claude-sonnet-5, launched with the Agent tool in the background - Parent session on
claude-opus-5 - Task: write a single module of roughly 900 lines
Numbers, taken from the subagent's own transcript (~/.claude/projects/<project>/<sessionId>/subagents/agent-<id>.jsonl)
| | |
|---|---|
| Assistant turns | 39 |
| Turns with stop_reason: max_tokens | 4, each exactly 64000 output tokens |
| Output tokens total | 258299 |
| Thinking tokens | 256478, or 99.3% of all output |
| Tool calls after the first 29 seconds | 4, all inside one 14 second window |
| Elapsed from the end of the working phase to the last turn | 38 minutes |
| Files written | none |
| Final assistant text | none |
Timeline, abridged, timestamps as recorded
15:38:42 agent starts, 29 turns of Read and Bash in 29 seconds, ordinary work
15:39:11 last tool call of the working phase (Read)
15:48:09 stop_reason=max_tokens output_tokens=64000 thinking=63998 no tool call
15:57:56 stop_reason=max_tokens output_tokens=64000 thinking=63999 no tool call
15:58:17 four tool calls in 14 seconds, brief recovery
16:07:59 stop_reason=max_tokens output_tokens=64000 thinking=63999 no tool call
16:17:32 stop_reason=max_tokens output_tokens=64000 thinking=64000 no tool call
transcript ends here, no file, no final text
The agent was composing the whole 900 line file inside its reasoning, hitting the 64k ceiling, and starting over. It did this four times.
Why nothing caught it
The async stall watchdog resets on every stream chunk and on a roughly 1 Hz progress tick, mechanism described in #85265. Thinking tokens are stream chunks, so a turn that emits 64000 thinking tokens and nothing else looks perfectly healthy to it. From the parent session there is no signal either: no tool call, no partial output, no notification that a turn ended on max_tokens. The parent cannot tell this apart from an agent doing careful work.
How this differs from the neighbouring reports
- #83848, the transcript stops mid-conversation. Here it does not stop, it keeps producing complete
max_tokensturns. - #79865 and #85265, silence after a
tool_result, agent killed by the 600s watchdog. Here tokens flow the whole time and the watchdog never fires at all. - #82803 and #79415, degenerate or corrupted output consuming
max_tokens. Here the output is well formed reasoning, it is simply never externalised.
What Should Happen?
stop_reason: max_tokenson a subagent turn should surface to the parent session immediately. The information is already written to the transcript and currently reaches nobody.- Repeated
max_tokensturns with no tool call between them should count as a stall. Two in a row is already pathological, four is unambiguous. - The stall watchdog should not treat a thinking-only turn as progress, or should hold a separate budget for it.
Error Messages/Logs
None, and that is the point of this report. The only trace anywhere is "stop_reason":"max_tokens" inside the subagent transcript.
Steps to Reproduce
- Launch a background subagent with the Agent tool and ask it for a single file of roughly 900 lines in one shot, with no instruction about how to write it.
- Watch the parent session. Nothing arrives, no error and no notification.
- Read
~/.claude/projects/<project>/<sessionId>/subagents/agent-<id>.jsonland grep for"stop_reason":"max_tokens".
Honest caveat on reproducibility: I observed this once, with the transcript above as evidence. I did not then run a controlled repeat, because the relaunch used the workaround below and completed normally. So the cause is strongly supported by the transcript but the trigger condition, asking for the entire file in one response, is inferred rather than proven across repeats.
Workaround
At the prompt level, and it is reliable. Instruct the subagent to write the file incrementally: first section on disk within 10 minutes, then extend with Edit in chunks of at most 300 lines, and explicitly never compose the whole file in thinking before writing it. The identical task then finished in 12 minutes with the file written.
For detection from outside, two or more max_tokens turns with no tool call between them is a reliable signature. A small script that reads the transcript tail and reports time since the last tool call, tool_use ids with no matching tool_result, and the count of max_tokens turns is here together with a writeup: https://gist.github.com/Sundwell/540e515f4ed098ecb0c785e87e024c20
Related: #91093 asks for a liveness signal for background subagents. Surfacing max_tokens would be the cheapest possible version of that.
Claude Model
claude-sonnet-5 for the subagent, claude-opus-5 for the parent session
Is this a regression?
Not sure, first time running a task of this shape
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗