[BUG] A pasted image is re-rendered as a file pointer on the next request: one full prompt-cache rewrite, and the model loses the image
Version: 2.1.259, macOS, OAuth (Max), claude-fable-5-1[1m], promptCacheTtl: "1h".
What happens
Paste a screenshot into the prompt. On that request the image goes up as a base64 image block. On the next request the same history message is rendered as text: [Image: source: ~/.claude/image-cache/<session>/1.png]. Both forms are in the transcript for the same message. The prompt-cache prefix breaks at that message, and everything after it is rewritten.
Receipt from a 232k-token session, one screenshot pasted at 03:49:41:
| request | cache_read | cache_creation (1h) | note |
|---|---|---|---|
| turn before | 231,751 | 828 | hit |
| turn with the image | 83,834 | 158,694 | miss: prefix intact only up to the image message |
| turns after | climbing again | small | hits |
That is one ~$3 write on Fable 5.1 for a 136 KB PNG. The other typed turns in that session (5 before, 60+ after) were all full hits, so nothing else moved.
Why the trade is wrong
The re-render exists to avoid resending ~1.5k image tokens per turn. But:
- Keeping the base64 block byte-identical in history costs ~1.5k cached tokens per turn — cents over a hundred turns.
- Replacing it costs one rewrite of everything after it, at the largest context the session has reached, once per pasted image.
- After the swap the model no longer has the image. The pointer is text; the model does not re-read the file unless it chooses to
Readthe path. "What's causing this?" is answered once, then the screenshot is gone from context for the rest of the session.
So the current behavior is strictly dominated: more expensive and less capable than leaving the block alone.
Expected
Any of:
- Keep the image block byte-stable in history for the session; trim only at compaction (already a full rewrite).
- Send the pointer form from the first request and have the model
Readit, so the shape never changes. - If the swap must happen, do it only when the image is already older than the compaction horizon.
Workaround I'm using meanwhile
Deliver screenshots as a file path instead of a paste, so the model Reads the image: it then arrives as a tool result, is replayed byte-for-byte, keeps the cache, and stays in context for the session. I wrote a small menu bar app that makes the native screenshot shortcut produce a path on the clipboard, downscaled to the 1568 px ceiling, with a crop option since cropping is the only lever that reduces image tokens: https://github.com/ArshansGithub/shotpath. It's a workaround for this issue, not a substitute for fixing the re-render.
Related
2.1.257 fixed "prompt-cache misses on every turn in long screenshot-heavy sessions"; this is the one-time residual of the same mechanism. Same family as #83913 (history mutated on rebuild). Full investigation with wire captures: #91707.
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗