Summary
Built-in tokens-input, tokens-output, and tokens-total widgets permanently show 0 for long-lived sessions whose transcript JSONL is larger than Node's maximum string length (~512MB / 0x1fffffe8 characters), while other metrics (session cost, custom streaming parsers) continue to work.
Environment
- ccstatusline: 2.2.27 (latest on npm)
- Claude Code: 2.1.226
- OS: Windows 11
- Node: via nvm4w global install
Reproduction
- Open a Claude Code session whose
transcript_path points at a very large JSONL (observed: ~844 MB under ~/.claude/projects/.../<session>.jsonl).
- Status line configured with
tokens-input, tokens-output, tokens-total, and session-cost.
- Observe:
In: 0 Out: 0 Total: 0
Cost: $... still correct (comes from statusline stdin payload, not transcript scan)
- Custom commands that stream/chunk-read the same transcript still report real cache totals
Minimal reproduction with the installed binary:
# payload with transcript_path set to an ~844MB session jsonl
echo '{"transcript_path":".../huge.jsonl","cost":{"total_cost_usd":1537.20},"model":{"display_name":"Sonnet 5"},"version":"2.1.226"}' | ccstatusline
# → In: 0 Out: 0 Total: 0 (Cost still shown)
Direct Node repro of the underlying failure:
fs.readFileSync(hugeTranscriptPath, 'utf8')
// Error: Cannot create a string longer than 0x1fffffe8 characters
Root cause
getTokenMetrics() (and readJsonlLines) loads the entire transcript with fs.readFile(..., "utf-8"):
async function readJsonlLines(filePath: string) {
const content = await readFile(filePath, "utf-8");
return splitJsonlContent(content);
}
When the file exceeds Node's max string size, that throws. getTokenMetrics swallows the error and returns zeros:
} catch {
return { inputTokens: 0, outputTokens: 0, /* ... */ totalTokens: 0, contextLength: 0 };
}
Token widgets then prefer context.tokenMetrics (even when all zeros) and never fall back to context_window totals:
if (context.tokenMetrics) {
return formatRawOrLabeledValue(item, "In: ", formatTokens(context.tokenMetrics.inputTokens));
}
Expected behavior
- Token widgets should keep working for multi-hundred-MB / multi-GB transcripts.
- Prefer streaming / chunked line iteration (or incremental offset cache) instead of materializing the whole file as one string.
- Failures should not silently look like a real zero-token session if the file is unreadable / unparsable as a whole (optional: leave metrics null so widgets can fall back or hide).
Evidence from a real session
| Source |
Result on ~844MB transcript |
| Built-in tokens-input/output/total |
0 / 0 / 0 |
| session-cost (stdin) |
correct |
| Custom streaming statusline parser |
ReadCache ~23704.1M etc. still correct |
| Same install on a ~22MB transcript |
tokens work normally |
Suggested fix
- Replace full-file
readFile + string split in the token-metrics path with a streaming line reader (chunked fs.createReadStream / readline, or fixed-size buffer with newline scanning).
- Apply the same approach anywhere else that calls
readJsonlLines / readJsonlLinesSync for session-scoped metrics that can grow without bound (speed metrics, session clock, compaction, etc.) so those do not hit the same ceiling later.
- Keep the existing stop_reason filtering / compaction boundary logic.
Happy to open a PR with a streaming getTokenMetrics implementation.
Related
This is the same class of problem as any tool that does readFileSync(path, 'utf8') on Claude Code session transcripts after very long / high-cache sessions.
Summary
Built-in
tokens-input,tokens-output, andtokens-totalwidgets permanently show 0 for long-lived sessions whose transcript JSONL is larger than Node's maximum string length (~512MB /0x1fffffe8characters), while other metrics (session cost, custom streaming parsers) continue to work.Environment
Reproduction
transcript_pathpoints at a very large JSONL (observed: ~844 MB under~/.claude/projects/.../<session>.jsonl).tokens-input,tokens-output,tokens-total, andsession-cost.In: 0Out: 0Total: 0Cost: $...still correct (comes from statusline stdin payload, not transcript scan)Minimal reproduction with the installed binary:
Direct Node repro of the underlying failure:
Root cause
getTokenMetrics()(andreadJsonlLines) loads the entire transcript withfs.readFile(..., "utf-8"):When the file exceeds Node's max string size, that throws.
getTokenMetricsswallows the error and returns zeros:Token widgets then prefer
context.tokenMetrics(even when all zeros) and never fall back tocontext_windowtotals:Expected behavior
Evidence from a real session
Suggested fix
readFile+ string split in the token-metrics path with a streaming line reader (chunkedfs.createReadStream/readline, or fixed-size buffer with newline scanning).readJsonlLines/readJsonlLinesSyncfor session-scoped metrics that can grow without bound (speed metrics, session clock, compaction, etc.) so those do not hit the same ceiling later.Happy to open a PR with a streaming
getTokenMetricsimplementation.Related
This is the same class of problem as any tool that does
readFileSync(path, 'utf8')on Claude Code session transcripts after very long / high-cache sessions.