[FEATURE] Persist oversized-image strip so synthetic placeholder is not re-emitted every turn
Preflight
- [x] I searched existing issues and didn't find an exact duplicate (closest: #42256, see below)
- [x] This is a single feature request
Problem Statement
The image-recovery work that landed in 2.1.121 (failed image blocks degrade to a text placeholder instead of crashing the session) and 2.1.126 (oversized images in history are stripped and the request retried) is genuinely great — sessions that used to be permanently bricked now keep going.
What is still rough: the recovery does not persist. Every subsequent turn re-encounters the same oversized image at the same position in history, re-strips it, and re-emits the synthetic placeholder assistant turn:
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.
In a single real session at 2.1.143 I observed 48 emissions of that exact placeholder, each one a separate synthetic assistant message in the transcript, each tied to a fresh req_011... ID and a 400 from messages.34.content.1.image.source.base64.data. They all reference the same offending image at the same position — the strip is per-turn, not per-session.
Cost: each emission is a redundant 400 round-trip, plus a synthetic message that takes up a turn slot and burns cache (the model sees a noisier history). In a long session it crowds real context out and confuses downstream tools that scan transcripts.
Proposed Solution
After the first successful strip-and-retry, persist the stripped state to the session transcript so the offending image is gone for the rest of the session. Subsequent turns should not re-trigger the 400 or re-emit the placeholder.
Two reasonable shapes:
- Mutate history in place. After a strip-and-retry succeeds, rewrite the original message at that position to remove the image block (or replace it with the text placeholder), so the next turn sends clean history.
- Collapse into a single marker. Keep the original message intact but mark the image block as "known bad, do not re-send" in session state, and emit a single
(prior image removed from context)system note on the first occurrence. Subsequent turns skip the block silently.
Either shape would have prevented 47 of the 48 emissions in my session.
Alternative Solutions
/clear— works, but loses all session context. Not acceptable for long-running work.- Manually editing the JSONL transcript on disk — possible (
isApiErrorMessage: trueandmodel: "<synthetic>"are easy to grep for), but fragile and not something a normal user would do. - Running
/compact— drops the offending message along with everything else, same downside as/clear.
Priority
Medium. The 2.1.121/2.1.126 work already prevents the worst outcome (session death). This is a quality-of-life follow-on, not a blocker.
Feature Category
Performance (token / round-trip waste) and Core (session state persistence).
Use Case Example
- Long-running session, hours of context built up.
- User pastes a 3000×4000 screenshot for diff review.
- API returns 400 — image exceeds 2000px on a dimension.
- Harness strips it, retries successfully, session continues. Great.
- Every subsequent user turn: same image still at position 34 of history. Same 400. Same synthetic placeholder appended.
- After 50 turns of normal work, the transcript has 50 copies of the placeholder, the model has been hit with 50 redundant 400s, and the user has no idea why their session feels noisier than it should.
Related Issues
- #42256 — "Read tool sends oversized images to API, then re-sends them on every subsequent message." Same underlying root cause (oversized images persist in history), filed before the 2.1.121/2.1.126 recovery landed. That issue's worst symptom — the session is unusable — has been addressed. This issue is the residual: the recovery itself replays. Happy to close in favor of #42256 if maintainers prefer; flagging here so the dup-detection bot has context.
- #34566, #56934 — related but distinct (session crashes / no recovery). The new fixes address those; mine is downstream.
Environment
- Claude Code Version: 2.1.143
- Platform: Max subscription
- Operating System: macOS 26.4.1 (Apple M2 Ultra, 128 GB)
- VS Code: 1.120.0
- VS Code extension: anthropic.claude-code@2.1.143
- Terminal/Shell: VS Code integrated webview (zsh)
Additional Context
Sample sanitized log lines from the offending transcript (one session, 48 emissions, all with the same upstream error):
"type":"assistant","model":"<synthetic>",
"content":[{"type":"text","text":"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."}],
"error":"invalid_request",
"errorDetails":"400 {\"type\":\"error\",\"error\":{\"type\":\"invalid_request_error\",\"message\":\"messages.34.content.1.image.source.base64.data: At least one of the image dimensions exceed max allowed size for many-image requests: 2000 pixels\"},\"request_id\":\"req_exampleABC123\"}",
"isApiErrorMessage":true
messages.34.content.1.image is the same position across all 48 emissions, which is what makes the per-turn re-strip obvious.
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗