[BUG] sum of output_tokens from assistant messages returned by claude-agent-sdk query() doesn't equal output_tokens of the "result" message
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?
When iterating over messages generated by the query() method of @anthropic-ai/claude-agent-sdk, I collect token usage data from the message.usage of each assistant message and sum their values. However, the total sum of all output_tokens doesn't equal the value of output_tokens in the final result message.
I'm using @anthropic-ai/claude-agent-sdk version 0.2.84.
Model: claude-opus-4-6, but this happens for other models too.
I've followed the rules described in the https://platform.claude.com/docs/en/agent-sdk/cost-tracking documentation regarding skipping duplicate usages of messages with the same ID.
I discovered that when setting includePartialMessages to true and counting output_tokens from stream_event messages of type message_delta (instead of from assistant messages), I get the correct sum for output_tokens.
I've created a simple Node.js script available in this gist that calls query() with a simple prompt to test token usage and print the results.
Running query() with options.includePartialMessages: false
The sum of output_tokens from assistant messages doesn't equal the value of output_tokens in the final result message.
MESSAGE USAGE BREAKDOWN
==============================================================================
Message ID | Input | Cache Create | Cache Read | Output
------------------------------------------------------------------------------
msg_01Ea9T1t5eDLV7BZyVjd22bu | 3 | 2197 | 7233 | 30
msg_01NjzSe7a9fHiBq9nWNKWFLZ | 3 | 1137 | 9430 | 47
msg_013WH4Ksf9ZWSokidLhnypYq | 1 | 100 | 10567 | 1
------------------------------------------------------------------------------
Total | 7 | 3434 | 27230 | 78
------------------------------------------------------------------------------
type: result | 7 | 3434 | 27230 | 275
==============================================================================
TOKEN USAGE COMPARISON
========================================================
Token Type | Computed | Result
--------------------------------------------------------
✓ Input Tokens | 7 | 7
✓ Cache Creation Tokens | 3434 | 3434
✓ Cache Read Tokens | 27230 | 27230
✗ Output Tokens | 78 | 275 (diff: -197)
========================================================
✗ Discrepancies in token counts detected
Running query() with options.includePartialMessages: true
The sum of output_tokens ONLY from stream_event messages with event type message_delta equals the value of output_tokens in the final result message.
Tokens in brackets [] are ignored when computing totals.
MESSAGE USAGE BREAKDOWN
==============================================================================
Message ID | Input | Cache Create | Cache Read | Output
------------------------------------------------------------------------------
msg_01EdnokfMQShjSdfXjGvHivj | 3 | 2197 | 7233 | [24]
type: message_delta | [3] | [2197] | [7233] | 114
msg_01WN1oaCiJBx7yZTEUwHjENZ | 3 | 1662 | 8402 | [53]
type: message_delta | [3] | [1662] | [8402] | 97
msg_01YHKzFDhysvSK1BLRxenY1M | 1 | 254 | 10064 | [7]
type: message_delta | [1] | [254] | [10064] | 45
------------------------------------------------------------------------------
Total | 7 | 4113 | 25699 | 256
------------------------------------------------------------------------------
type: result | 7 | 4113 | 25699 | 256
==============================================================================
TOKEN USAGE COMPARISON
========================================================
Token Type | Computed | Result
--------------------------------------------------------
✓ Input Tokens | 7 | 7
✓ Cache Creation Tokens | 4113 | 4113
✓ Cache Read Tokens | 25699 | 25699
✓ Output Tokens | 256 | 256
========================================================
✓ All token counts match!
```
### What Should Happen?
When using `query()` with `options.includePartialMessages: false`, the sum of `message.usage.output_tokens` from all unique `assistant` messages (deduplicated by message ID, as documented in the cost tracking guide) should equal the `usage.output_tokens` of the final `result` message.
This matches the expected behavior for `input_tokens`, `cache_creation_input_tokens`, and `cache_read_input_tokens`, which all sum correctly, only `output_tokens` shows this discrepancy.
### Error Messages/Logs
```shell
Steps to Reproduce
Steps to Reproduce:
- Run the provided gist script with
includePartialMessagesset tofalse(default in the script). - Observe the MESSAGE USAGE BREAKDOWN table - note the "Total" row showing summed
output_tokensfrom allassistantmessages. - Compare this total with the "type: result" row showing the final result message's
output_tokens. - Check the TOKEN USAGE COMPARISON section at the bottom.
Expected Result:
- The "Total" and "type: result" rows should have matching
output_tokensvalues. - The script should print
✓ All token counts match!
Actual Result:
- The
output_tokensvalues don't match. - The script prints
✗ Discrepancies in token counts detected
Claude Model
Opus
Is this a regression?
No, this never worked
Last Working Version
_No response_
Claude Code Version
2.1.84
Platform
Anthropic API
Operating System
macOS
Terminal/Shell
Terminal.app (macOS)
Additional Information
_No response_
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗