[BUG] TodoWrite re-stamps a front-of-context block on every update, invalidating the entire prompt cache (full ~900k-token rewrite per todo update)

Open 💬 0 comments Opened Jun 13, 2026 by burakace

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?

Every TodoWrite update invalidates the entire prompt cache and forces a full
cache_creation rewrite of the whole context (often 800k-900k tokens) at 1.25x,
for near-zero output.

The todo_reminder block lives near the front of the conversation (in the cached
prefix). On each update TodoWrite re-stamps that whole block, which changes the
cached-prefix fingerprint and invalidates everything after the system prompt -
so cache_read collapses to ~system size and the rest is re-written.

It fires once per item update, so a session where Claude works through a todo
list produces dozens of full rewrites in minutes. In my own logs this is the
single largest driver of 5-hour-quota spikes (~93% of my multi-million-token
spikes; one 14-minute window burned 16.7M tokens across 20 rewrites).

Critical detail: it busts even when the todo content is byte-IDENTICAL between
updates (same hash), so this is a caching bug, not the list legitimately changing.

What Should Happen?

Updating the todo list - especially with identical content - should NOT
invalidate the prompt cache. TodoWrite should patch the list in place (or keep
the block out of the cached prefix / append-only) so the cached prefix stays
byte-stable and subsequent turns get cache_read, not a full cache_creation.

Error Messages/Logs

One 14-minute window (deduped by message.id). Each TodoWrite update is followed
by a full rewrite; the todo content hash is identical on all 20:

19:23:50 TodoWrite update  todo-hash=d75171  -> 19:24:06  cache_creation=770,583  cache_read=23,090
19:24:35 TodoWrite update  todo-hash=d75171  -> 19:24:43  cache_creation=777,074  cache_read=23,090
19:25:12 TodoWrite update  todo-hash=d75171  -> 19:25:20  cache_creation=783,119  cache_read=23,090
   ... 20 updates -> 20 full rewrites, 1:1, every hash = d75171 ...
19:37:30 TodoWrite update  todo-hash=d75171  -> 19:37:40  cache_creation=897,340  cache_read=23,090

Sum: 16,686,355 tokens in 14 minutes. cache_read pinned at 23,090 (system only)
on all 20 = entire context after system rewritten each time.

Steps to Reproduce

  1. Start a session and build up a warm cache (a few hundred k of context - e.g.

read several files so the conversation grows).

  1. Ask Claude to create a todo list and work through several items, updating the

list (marking items in_progress/completed) as it goes.

  1. Watch usage / the prompt-cache fields per turn (/status, or the session JSONL

usage objects).

Observed: each TodoWrite update yields a turn with large cache_creation and
cache_read collapsed to ~system size (a full rewrite). 5-hour usage climbs fast
for near-zero output. Reproduces with identical todo content.

Claude Model

Opus

Is this a regression?

I don't know

Last Working Version

_No response_

Claude Code Version

2.1.173

Platform

Anthropic API

Operating System

Windows

Terminal/Shell

VS Code integrated terminal

Additional Information

SCALE - 16 spikes >=2M tokens over ~2 weeks (~101M tokens total):
15 of 16 are TodoWrite; the 16th was a normal idle-resume cold write.
By tokens, ~93% TodoWrite. By individual rewrites, 128/146 (88%) had a
TodoWrite update within 60s before; the biggest spikes are a clean 100%
(16.7M = 20/20, 9.2M = 11/11). Avg gap between rewrites 43-112s = active
work, NOT idle TTL expiry.

LIKELY ROOT CAUSE: #2250 (TodoWrite overwrites the entire list every update,
rather than patching one item) - that whole-list overwrite regenerates the
front block and changes the cached-prefix fingerprint. Mechanism overlaps
#27048 / #42338 (attachment/meta blocks busting the prefix on re-render).
This is NOT #40524 (that is the separate cch=00000 sentinel bug).

WORKAROUND (removed ~93% of my spikes) - in ~/.claude/settings.json:
"todoFeatureEnabled": false

  • a PreToolUse deny hook on TodoWrite:

{
"matcher": "TodoWrite",
"hooks": [
{ "type": "command",
"command": "echo '{\"hookSpecificOutput\":{\"hookEventName\":\"PreToolUse\",\"permissionDecision\":\"deny\",\"permissionDecisionReason\":\"TodoWrite disabled to prevent prompt-cache busts\"}}'" }
]
}
Track tasks in a markdown file instead - plain file edits don't inject a
cache-busting block.

View original on GitHub ↗