[BUG] Output tokens incorrectly recorded in JSONL - only partial streaming values saved
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?
[Bug] Output tokens incorrectly recorded in JSONL - only partial streaming values saved
Description
Output tokens are being severely undercounted in the JSONL session files. Analysis shows that only intermediate streaming chunks (with output_tokens: 1 and stop_reason: null) are being recorded, while the final completed response containing the actual output token count is not saved.
Environment
- Claude Code Version: 2.1.29
- OS: Windows 11
- Model: claude-opus-4-5-20251101 (also affects haiku-4-5, sonnet-4-5)
Evidence
1. ccusage shows extremely low output tokens
npx ccusage@latest daily --json
Result:
| Date | Input | Output | Cache Create | Cache Read | Total |
|------|-------|--------|--------------|------------|-------|
| 2026-02-02 | 67,588 | 5,142 | 6,119,113 | 79,205,298 | 85,397,141 |
| 2026-02-03 | 18,908 | 500 | 702,132 | 6,974,880 | 7,696,420 |
Output tokens represent only 0.006% of total tokens, which is impossibly low for normal Claude Code usage with tool calls and responses.
2. JSONL analysis shows the root cause
Checking output_tokens distribution across session files:
grep -o '"output_tokens":[0-9]*' session.jsonl | sort | uniq -c | sort -rn
Result:
26 "output_tokens":1
6 "output_tokens":8
3 "output_tokens":5
3 "output_tokens":2
2 "output_tokens":6
Almost all recorded output_tokens values are 1!
3. Raw JSONL entries show intermediate streaming state
Every assistant message in JSONL has:
{
"message": {
"stop_reason": null,
"usage": {
"input_tokens": 8,
"output_tokens": 1,
"cache_creation_input_tokens": 6127,
"cache_read_input_tokens": 53499
}
}
}
Note:
stop_reason: nullindicates this is a streaming intermediate chunk, not the final responseoutput_tokens: 1is the token count at that streaming moment, not the final count
Expected Behavior
The JSONL should record the final usage statistics when stop_reason: "end_turn", containing the actual total output tokens generated.
Actual Behavior
Only intermediate streaming chunks are saved to JSONL, with:
stop_reason: nulloutput_tokens: 1(or other very small values)
The final response with the actual token count is never recorded.
Impact
- ccusage and other analytics tools report drastically incorrect output token usage
- Cost estimation is inaccurate (output tokens are typically more expensive)
- Usage monitoring is unreliable for subscription users tracking their limits
Steps to Reproduce
- Start a Claude Code session
- Have a conversation with multiple tool uses and responses
- Check the JSONL file at
~/.claude/projects/<project>/<session-id>.jsonl - Search for
output_tokensvalues - they will all be 1 or very small numbers - Check that all entries have
"stop_reason": null
Additional Context
This issue was discovered while investigating why ccusage (ryoppippi/ccusage) was showing unexpectedly low output token counts. The investigation revealed the issue is in Claude Code's JSONL recording, not in ccusage's parsing.
Related ccusage issues that may stem from this bug:
- https://github.com/ryoppippi/ccusage/issues/313 (sub-task token tracking)
- https://github.com/ryoppippi/ccusage/issues/487 (inaccurate live token monitor)
What Should Happen?
The JSONL session files should record the final usage statistics when the response is complete (stop_reason: "end_turn"), containing
the actual total output tokens generated for each assistant message.
For example, if Claude generates a 500-token response, the JSONL should record: 8
{
"stop_reason": "end_turn",
"usage": {
"output_tokens": 500
}
}
Error Messages/Logs
Steps to Reproduce
- Start a Claude Code session (version 2.1.29)
- Have a conversation with multiple tool uses and responses
- Check the JSONL file at ~/.claude/projects/<project>/<session-id>.jsonl
- Run: grep -o '"output_tokens":[0-9]*' session.jsonl | sort | uniq -c
- Observe that almost all output_tokens values are 1 or very small numbers
- Check that all entries have "stop_reason": null (intermediate streaming state)
- Run: npx ccusage@latest daily --json
- Observe output tokens are severely undercounted (0.006% of total in my case)
Claude Model
None
Is this a regression?
Yes, this worked in a previous version
Last Working Version
_No response_
Claude Code Version
2.1.29
Platform
Anthropic API
Operating System
Windows
Terminal/Shell
Warp
Additional Information
_No response_
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗