I lost a whole coding session this morning to a single broken image, and the way it failed is worth writing down, because it is invisible, it is expensive, and it is almost certainly hitting other people who have no idea why their usage is evaporating.
Here is the short version, and then the proof.
The symptom
Partway through a long Claude Code session, you see this:
API Error: an image in the conversation could not be processed and was removed.
Re-read the file with a different approach if you still need it.
That message reads like an all-clear. An image went bad, the tool removed it, carry on. So you carry on. And over the next two or three replies your session budget falls off a cliff for no visible reason. Mine went from basically full to nearly empty in three turns.
The message is lying to you. The image was not removed. It was dropped from that one failed request and left sitting in your conversation, where it gets replayed on every turn that follows.
Why "removed" costs you 12x
To see why that is so expensive, you need one fact about how these sessions are billed: prompt caching.
Every turn, the whole prior conversation is sent to the model again. Normally almost all of it is cached - the model recognizes the identical prefix from last turn and reads it back cheaply. Cached reads are billed at roughly a tenth of the normal token rate. That discount is the only reason a long session stays affordable.
A poisoned image breaks the cache. The bad image is still in the replayed history, it fails again, and the matching prefix no longer lines up - so instead of a cheap cache read, the entire history is re-processed as a fresh cache write. Cache writes are billed at about 1.25x. You do not need a calculator to feel that: you flip from paying 0.1x to paying 1.25x on your whole conversation, every turn, until you escape.
That is roughly twelve times the cost per turn - and it repeats, because the image never leaves.
Proving it with the receipts
I did not want to just assert this, so I went into the session log. Claude Code writes every turn to a local .jsonl file, and each assistant turn carries a usage record with two numbers that tell the whole story: cache_read_input_tokens (the cheap path) and cache_creation_input_tokens (the expensive one).
Here is the healthy part of the session. The big ~790,000-token history is being read from cache; only a couple thousand new tokens are written each turn:
| turn | cache_read | cache_creation |
|---|---|---|
| normal | 769,755 | 762 |
| normal | 787,296 | 1,401 |
| last good turn | 795,596 | 2,942 |
Then the "could not be processed" error fires. Watch the two columns swap places:
| turn | cache_read | cache_creation |
|---|---|---|
| after the error | 23,760 | 763,100 |
| next turn | 26,190 | 759,539 |
Cache reads collapse from ~790k to ~24k. Cache creation explodes from ~2k to ~763k. The entire conversation is now being rebuilt from scratch on every turn, at the expensive rate, and the error keeps firing because the image is still there. Three turns of that emptied a session that started at full budget.
That is not a rounding error or a bad-luck spike. It is the same mechanism, measured, turn after turn.
What to do about it right now
Until this is fixed on the platform side, the workaround is simple and you should burn it into muscle memory:
an image in the conversation could not be processed and was removed, run /clear or start a new conversation before you send another message. Do not reply into that session again - every further turn is the 12x hit.
The trap is that the error message actively encourages you to keep going. Do not. One poisoned turn is survivable; three is what wipes you out.
The real fix
The proper fix belongs upstream: when the API reports an image as unprocessable, the client should actually evict it from the persisted conversation - not just from the one request that failed - so it stops replaying and stops re-breaking the cache. A single bad image should never force a full-history re-bill on every subsequent turn, and the "was removed" message should be true.
I wrote all of this up in detail, with the full token timeline and reproduction steps, and filed it on the official tracker. If it has bitten you too, an upvote or a "same here" genuinely helps it get noticed.
Read / upvote the bug report on GitHub Discuss it on Reddit
The tools we build on are going to have sharp edges like this for a while. The best thing any of us can do is catch one, measure it precisely enough that it cannot be waved away, and hand the maintainers something they can act on. This one took an afternoon and a look at the receipts.