Subagent transcripts record start-of-stream `output_tokens` and no `stop_reason` for most turns
What's wrong
In subagent transcripts (~/.claude/projects/<project>/<session>/subagents/agent-*.jsonl), most assistant turns are written before the response finishes streaming. Every line of the turn carries the message.usage from the start of the stream, so output_tokens stays at its initial value (1 to 59) even when the reply is thousands of tokens long. The final stop_reason and the final usage are never written for that turn.
Main-session transcripts are not affected.
Evidence
Claude Code 2.1.281 and 2.1.282 on Linux, 24 to 27 September 2026, on a Claude subscription:
- 402 of 539 subagent turns (75%) have
message.stop_reason: nullon every line, nousage.output_tokens_details, and identicalusageon every line. Theiroutput_tokensrange from 1 to 59. - The other 137 subagent turns have
stop_reasonset (end_turnortool_use) on their final line, carryoutput_tokens_details, and theiroutput_tokensgrows on that line. - There are no exceptions to that split: a turn either has all three signs of a finished write or none of them.
- Main sessions: 2 of 2,004 turns show the same pattern.
- It affects every subagent type (built-in and custom), foreground and background alike.
- One example: a reply of about 11,700 characters was recorded with
output_tokens: 2. - No other file under
~/.claudecarries the final count. For a background run, the parent's tool result reportsisAsyncwith no totals. For a foreground run, itsusagerepeats only the subagent's last turn. - The OpenTelemetry
claude_code.api_requestevent for the samerequest_iddoes carry the finaloutput_tokens, so the correct figure exists at runtime; it just never reaches the transcript.
Why it matters
Anything that reads transcripts to measure usage or cost, including community usage tools and our own, under-counts subagent output. Output tokens are the most expensive class, so the under-count is large and silent.
Expected
Each subagent assistant turn's final transcript line carries the response's final usage and stop_reason, as main-session transcripts do.
Steps to reproduce
- Run a session that dispatches several subagents, some in the background.
- In the subagent JSONL files, group assistant lines by
requestId. - Count the turns whose lines all have
stop_reason: nulland identicalusage. Compare theiroutput_tokensagainst the length of their content.
Showing cached comments. Read the full discussion on GitHub ↗
3 Comments
Same bug here on macOS with Amazon Bedrock as the provider, so it isn't specific to Linux or subscriptions. I can also narrow when it started: last good 2.1.277, first affected 2.1.278, and it's still present in 2.1.289.
Sub-agent responses where every line has
stop_reason: null(one install, ~46k responses):Two independent checks show the transcript is short, not the API response:
requestIds to Bedrock's own per-request records (CloudTrail) for one 24 h window. Main-session requests matched exactly (1,217/1,217). Sub-agent requests: 1,018,953 output tokens provider-side against 23,407 in the transcripts.cost-stateline reports 877,111 output tokens, while the transcripts sum to 302,119. An unaffected 2.1.271 session with sub-agents agrees within 0.2%.Impact: for sub-agent-heavy work, summing transcript usage gave about 60% of the true input+output over 24 h.
Additional data from a second Linux install (VS Code extension, x64, Claude subscription, heavy use of background subagents). This points to the regression starting at different versions on different installs.
Subagent requests where no line carries a non-null
stop_reason(uniquemessage.id, ~69k requests, 2026-08-06 → 2026-10-06):| Version | Requests without final usage |
|---|---|
| 2.1.220 – 2.1.241 | 4 – 23 % (the older race from #84223) |
| 2.1.247 – 2.1.267 | 44 – 92 % |
| 2.1.269 – 2.1.274 | 0.0 – 0.7 % |
| 2.1.278 | 0.3 % (3 / 865) |
| 2.1.280 | 0.7 % (13 / 1,816) |
| 2.1.281 | 0.0 % (0 / 3,118) |
| 2.1.282 | 88.7 % (635 / 716) |
| 2.1.283 | 96.2 % (661 / 687) |
| 2.1.284 | 91.3 % (981 / 1,075) |
| 2.1.285 | 96.7 % (3,993 / 4,128) |
| 2.1.286 | 96.1 % (1,923 / 2,001) |
| 2.1.289 | 96.1 % (424 / 441) |
Main-session requests: 0 % in every version.
Two things differ from the report above:
So the onset does not seem to be a single version boundary; maybe a staged rollout, a flag, or workload-dependent timing.
More observations:
toolUseResultcarries no usage at all. Its keys areagentId,canReadOutputFile,description,isAsync,outputFile,prompt,resolvedModelandstatus. The parent therefore offers no fallback either.claude_code.api_requestcarriesrequest_id, which equals the transcriptrequestId. Its four token fields matched finalized transcript records exactly on 2.1.281 (CLI) and 2.1.291 (extension binary), including subagent requests (query_source: agent:builtin:general-purpose).Related: #84223 (older, race-like variant at ~20 %).
Same issue on Windows 11, desktop app (
entrypoint: claude-desktop), so it is not Linux/macOS-only. Data from one user's local transcripts, 2026-09-21 → 2026-10-11. Responses are deduplicated bymessage.id+requestId.Responses with a closing row (non-null
stop_reason) / all responses:| Version | Main session | Subagents |
|---|---:|---:|
| 2.1.275 | 420/420 | 38/57 |
| 2.1.280 | 6542/6543 | 14708/14750 |
| 2.1.284 | 5507/5507 | 3435/7646 |
| 2.1.286 | 921/922 | 75/416 |
| 2.1.288 | 1552/1553 | 335/1634 |
| 2.1.289 | 1848/1848 | 714/2960 |
| 2.1.293 | 3563/3565 | 1223/3566 |
| 2.1.295 | 1144/1145 | 86/194 |
Findings:
input_tokens,cache_creation_input_tokensandcache_read_input_tokensare identical on both rows. Only output differs: median 7 tokens on the start row vs 588 on the closing row.output_tokens_details.thinking_tokensin subagents matches the missing closing rows almost 1:1. Per week it is 50 / 4451 / 6108 responses without thinking tokens vs 50 / 4452 / 6113 without a closing row.Practical effect: weekly subagent output looked like it fell from 13.8M to 3.1M tokens, while output per fully measured subagent response stayed flat (~1176 → 1286).