Extended thinking generates duplicate .jsonl log entries with identical input tokens, causing ~2-3x token inflation in usage tracking
Bug Description
When extended thinking is enabled, each API call is logged as 2–5 separate entries in the .jsonl conversation log, all sharing identical cache_read_input_tokens and cache_creation_input_tokens values. This causes the local usage counter to over-count token consumption by approximately 2×, and we suspect the rate-limit usage indicator shown in the UI is also affected (see Open Question below).
Environment
- Claude Code version: 2.1.77 (auto-updated to 2.1.88 shortly after; not yet verified on 2.1.88)
- OS: Linux (x64)
- Models affected:
claude-opus-4-6ANDclaude-sonnet-4-6(both confirmed) - Trigger: Extended thinking enabled — reproducible via
CLAUDE_CODE_EFFORT_LEVEL=max, or any configuration that activates extended thinking (settings.json thinking budget,/modelselection, etc.)
Trigger Conditions
The duplication occurs on every API call where extended thinking is active. Turns without thinking produce a single FINAL entry with no duplication.
Per API call, the log contains:
| Entry type | inference_geo | output_tokens | content types | has iterations/speed |
|---|---|---|---|---|
| PRELIM #1 | "not_available" | 0–40 (thinking tokens) | ["thinking"] | ✗ |
| PRELIM #2 (sometimes) | "not_available" | same value as PRELIM #1 | ["text"] or ["tool_use"] | ✗ |
| FINAL | "" | actual response tokens | ["tool_use"] / ["text"] | ✓ |
All entries for the same API call share identical cache_read_input_tokens and cache_creation_input_tokens. In multi-agent sessions with concurrent subagents, duplication reaches up to 7 entries per call.
Steps to Reproduce
- Set
CLAUDE_CODE_EFFORT_LEVEL=max(or enable extended thinking via any other config) - Start a session in any project directory
- Send a few messages that trigger tool use (e.g. ask Claude to read a file)
- Inspect
~/.claude/projects/<project>/<session-id>.jsonl - Find consecutive
type: "assistant"entries with identicalcache_read_input_tokens— the earlier entries will haveinference_geo: "not_available"and lackiterations/speedfields
Expected: One log entry per API call; or if streaming intermediaries are logged, only FINAL entry tokens count toward usage
Actual: 2–5 entries per call with identical input token counts logged
Quantified Impact (from session analysis)
Analysis of a full 5-hour window (929 logged assistant entries across 4 sessions, both Opus 4.6 and Sonnet 4.6):
| | Call count | Effective input tokens |
|---|---|---|
| FINAL entries only (actual cost) | 474 | 4,862,690 |
| PRELIM entries (duplication) | 455 | 4,706,607 |
| Total as logged | 929 | 9,569,298 |
| Inflation multiplier | — | 1.97× |
In the worst 20-minute window (multi-agent evaluation session):
| | Call count | Effective input tokens |
|---|---|---|
| FINAL only | 105 | 1,165,909 |
| PRELIM (duplication) | 138 | 1,913,550 |
| Total as logged | 243 | 3,079,459 |
| Inflation multiplier | — | 2.64× |
~49–62% of logged token consumption is phantom duplication.
Evidence from Logs
Single-agent example (same cr=27,874, three entries):
06:15:28 PRELIM out=34 content=["thinking"] inference_geo="not_available" (no iterations/speed)
06:15:29 PRELIM out=34 content=["text"] inference_geo="not_available" (no iterations/speed)
06:15:34 FINAL out=955 content=["tool_use"] inference_geo="" (has iterations/speed)
Multi-agent example (same cr=109,079, 7 entries):
06:13:09 out=8 content=["tool_use"] inference_geo="not_available"
06:13:09 out=8 content=["tool_use"] inference_geo="not_available"
06:13:10 out=8 content=["tool_use"] inference_geo="not_available"
06:13:10 out=8 content=["tool_use"] inference_geo="not_available"
06:13:11 out=8 content=["tool_use"] inference_geo="not_available"
06:13:11 out=8 content=["tool_use"] inference_geo="not_available"
06:13:12 out=606 content=["tool_use"] inference_geo="" ← FINAL
Open Question
It is unclear whether Anthropic's server-side rate limiter counts PRELIM entries separately or deduplicates them. The JSONL files are client-side logs. However:
- A 20-minute session showed ~30% of the 5-hour quota consumed
- Based on FINAL-only tokens, actual consumption should be ~11%
- This is consistent with a ~2.64× inflation reaching the rate-limit counter
We'd appreciate clarification on whether the rate-limit accounting is based on individual streaming events (PRELIM + FINAL) or per logical API call (FINAL only).
This issue has 7 comments on GitHub. Read the full discussion on GitHub ↗