Extended thinking generates duplicate .jsonl log entries with identical input tokens, causing ~2-3x token inflation in usage tracking

Status Fixed / completed
Reported on v2.1.77
Maintainer reply None cached
Activity 7 comments · opened Mar 31, 2026 · closed May 13, 2026

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 , 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-6 AND claude-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, /model selection, 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

  1. Set CLAUDE_CODE_EFFORT_LEVEL=max (or enable extended thinking via any other config)
  2. Start a session in any project directory
  3. Send a few messages that trigger tool use (e.g. ask Claude to read a file)
  4. Inspect ~/.claude/projects/<project>/<session-id>.jsonl
  5. Find consecutive type: "assistant" entries with identical cache_read_input_tokens — the earlier entries will have inference_geo: "not_available" and lack iterations/speed fields

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).

View original on GitHub ↗

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