[BUG] Output tokens incorrectly recorded in JSONL - only partial streaming values saved

Resolved 💬 2 comments Opened Feb 3, 2026 by shkwon86 Closed Feb 3, 2026

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: null indicates this is a streaming intermediate chunk, not the final response
  • output_tokens: 1 is 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: null
  • output_tokens: 1 (or other very small values)

The final response with the actual token count is never recorded.

Impact

  1. ccusage and other analytics tools report drastically incorrect output token usage
  2. Cost estimation is inaccurate (output tokens are typically more expensive)
  3. Usage monitoring is unreliable for subscription users tracking their limits

Steps to Reproduce

  1. Start a Claude Code session
  2. Have a conversation with multiple tool uses and responses
  3. Check the JSONL file at ~/.claude/projects/<project>/<session-id>.jsonl
  4. Search for output_tokens values - they will all be 1 or very small numbers
  5. 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:

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

  1. Start a Claude Code session (version 2.1.29)
  2. Have a conversation with multiple tool uses and responses
  3. Check the JSONL file at ~/.claude/projects/<project>/<session-id>.jsonl
  4. Run: grep -o '"output_tokens":[0-9]*' session.jsonl | sort | uniq -c
  5. Observe that almost all output_tokens values are 1 or very small numbers
  6. Check that all entries have "stop_reason": null (intermediate streaming state)
  7. Run: npx ccusage@latest daily --json
  8. 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_

View original on GitHub ↗

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