Summary
The Claude Code parser currently assumes each assistant JSONL line represents one distinct billable model response. In practice, Claude sessions can contain multiple assistant lines with the same message.id and identical message.usage, while splitting the response into separate content chunks (text, tool_use, etc.).
This causes tokmon to over-count Claude Code tokens and cost.
Evidence
Recent example:
- Session:
d7a03194-a9d6-4ed8-afb1-a20c5ff8fc82
- Claude Code version:
2.1.114
- Same
assistant.message.id appears across multiple lines with duplicated usage
For this session:
- Current parser totals:
input=76670, output=1566, cacheRead=178398
- Deduped by
assistant.message.id: input=36519, output=1085, cacheRead=124188
This is not limited to the latest Claude Code version. A scan of older sessions found the same pattern in prior versions too, including 2.1.69 and 2.1.101.
Examples found during investigation:
da699e25-09cc-46ab-b113-6af79297460e.jsonl (2.1.69): 238 assistant lines, 44 duplicate message-id lines
eac7bd69-cb14-43f8-8726-a4fc022bc619.jsonl (2.1.69): 147 assistant lines, 17 duplicate message-id lines
d7a03194-a9d6-4ed8-afb1-a20c5ff8fc82.jsonl (2.1.114): 8 assistant lines, 3 duplicate message-id lines
Current assumption in code
In src/parsers/claude-code.ts, parseClaudeSessionFile() currently:
- iterates every JSONL line
- treats every
type === "assistant" record as a separate token-bearing event
- adds
message.usage directly into session/model totals
That assumption is not format-safe.
Expected behavior
For Claude Code session parsing:
- token accounting should be deduplicated by logical assistant response, likely keyed by
assistant.message.id
- tool usage extraction should still include all content blocks, even if multiple lines belong to the same assistant response
- if a line is malformed or missing expected fields, parser should emit a warning rather than silently assuming the structure is valid
Proposed fix
- Update the Claude Code parser to track seen assistant message IDs per session.
- Only count
message.usage once per unique assistant response.
- Continue accumulating
tool_use blocks from all assistant lines.
- Add runtime validation / warnings around parser format assumptions so future Claude Code format changes are easier to detect.
Why this matters
This bug inflates Claude Code token and cost reporting, which directly undermines the dashboard's core purpose.
Summary
The Claude Code parser currently assumes each
assistantJSONL line represents one distinct billable model response. In practice, Claude sessions can contain multipleassistantlines with the samemessage.idand identicalmessage.usage, while splitting the response into separate content chunks (text,tool_use, etc.).This causes
tokmonto over-count Claude Code tokens and cost.Evidence
Recent example:
d7a03194-a9d6-4ed8-afb1-a20c5ff8fc822.1.114assistant.message.idappears across multiple lines with duplicatedusageFor this session:
input=76670,output=1566,cacheRead=178398assistant.message.id:input=36519,output=1085,cacheRead=124188This is not limited to the latest Claude Code version. A scan of older sessions found the same pattern in prior versions too, including
2.1.69and2.1.101.Examples found during investigation:
da699e25-09cc-46ab-b113-6af79297460e.jsonl(2.1.69): 238 assistant lines, 44 duplicate message-id lineseac7bd69-cb14-43f8-8726-a4fc022bc619.jsonl(2.1.69): 147 assistant lines, 17 duplicate message-id linesd7a03194-a9d6-4ed8-afb1-a20c5ff8fc82.jsonl(2.1.114): 8 assistant lines, 3 duplicate message-id linesCurrent assumption in code
In
src/parsers/claude-code.ts,parseClaudeSessionFile()currently:type === "assistant"record as a separate token-bearing eventmessage.usagedirectly into session/model totalsThat assumption is not format-safe.
Expected behavior
For Claude Code session parsing:
assistant.message.idProposed fix
message.usageonce per unique assistant response.tool_useblocks from all assistant lines.Why this matters
This bug inflates Claude Code token and cost reporting, which directly undermines the dashboard's core purpose.