Image processing failure silently passes oversized images into context, bricking sessions
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?
When the native image processor (sharp) fails to resize an oversized image (e.g., module not available, unsupported format, corrupt image), the error path silently passes the raw, unresized image into the conversation context. If the conversation has accumulated many images, this triggers the API's stricter many-image dimension limit (2000px), returning:
API Error: 400 "image dimensions exceed max allowed size for many-image requests: 2000 pixels"
Since the oversized image is now embedded in the conversation history, every subsequent request fails with the same error. The session is permanently bricked until the user runs /clear or restarts.
Root Cause
The image resize pipeline already correctly targets 2000x2000px (WB=2000, ZB=2000 in the bundled code). The problem is not the target dimension — it's what happens when the resize fails.
The image processing error classifier handles several failure modes (module not found, unsupported format, corrupt header, dimension exceeded, OOM, timeout, Vips errors), but when these errors occur during resize of an oversized image, the code falls through and includes the original oversized image in the message context anyway.
The v2.1.42 fix ("Fixed image dimension limit errors to suggest /compact") only added a text suggestion in the API error handler:
"An image in the conversation exceeds the dimension limit for many-image requests (2000px). Run /compact to remove old images from context"
This doesn't actually fix the session — the oversized image remains in context, and every subsequent API call (including any triggered by /compact) continues to fail with the same error.
Related: #16173, #13480, #2939
What Should Happen?
When the image processor fails to resize an oversized image, do not include the raw image in context. Instead, return a text block:
{
"type": "text",
"text": "Could not process image: dimensions exceed 2000px and the image processor failed. Please resize the image manually or use a smaller image."
}
This keeps the session healthy and gives the user actionable feedback.
Additionally, the API error handler for "image dimensions exceed max allowed size for many-image requests" should trigger auto-compact rather than just suggesting /compact, since at that point the session is already unrecoverable without intervention.
Steps to Reproduce
- Start a Claude Code session on a system where the native image processor may fail (or simulate a failure)
- Accumulate several images in the conversation through screenshots or the Read tool
- Include an image that exceeds 2000px in any dimension where the resize fails (e.g., unsupported format)
- The raw oversized image enters context silently
- The API returns the 400 "many-image" dimension error
- Every subsequent prompt fails with the same error — session is bricked
Claude Code Version
2.1.76
Platform
Anthropic API
Operating System
macOS
Terminal/Shell
zsh
Showing cached comments. Read the full discussion on GitHub ↗
12 Comments
Found 3 possible duplicate issues:
This issue will be automatically closed as a duplicate in 3 days.
🤖 Generated with Claude Code
The "session bricked by oversized image" problem is nasty because once the image is in context, every subsequent turn re-sends it to the API and fails.
Workaround: PreToolUse hook that blocks oversized image reads
~/.claude/hooks/image-size-guard.sh:Exit code 2 = hard block. Claude Code won't proceed with the Read, and the error message tells the model to resize first.
This prevents the image from ever entering context, which is the key — once it's in,
/compactis the only escape, and even that doesn't always work if the API error persists across compaction boundaries.Workaround that addresses this for the
Readtool path: aPreToolUsehook with subprocess proxy that keeps image data out of the conversation context entirely.The hook converts every image to max 800px JPEG before processing, so oversized images never reach the API. In proxy mode (default), a Haiku subprocess analyzes the image and returns text only — zero image data in context, no accumulation, no bricking.
Note: This only covers images loaded via the
Readtool. Images pasted/dragged into the terminal or injected viadata:imageURLs in stdout still bypass the hook. For those, ask Claude to read from disk instead of pasting.Gist: https://gist.github.com/justi/8265b84e70e8204a8e01dc9f99b8f1d0
---
cc @limbfao @yurukusa
Worth noting — this approach also helps in cases where the dimension check alone isn't enough:
If you're still hitting this after the size guard, give the proxy approach a go — it handles all of the above out of the box.
Also happens in the Claude Chrome web extension. Bricking the session here can not be recovered as there is no saved history to recover from.
<img width="731" height="433" alt="Image" src="https://github.com/user-attachments/assets/bb73d4f7-d7a4-4127-b740-81f1463400b6" />
Claude project file upload of image silently forces unwanted downscale. I want claude to see the full scale image (e.g. 1900x2400) so it sees it well enough to fully discuss. I have plenty of room in project capacity. Generally i prefer jpg.
Still reproducing on v2.1.112 (2026-04-16).
Concrete trigger that hit me: autonomous spawned session that Read() 26 competitor iPhone screenshots (.jpg, portrait 1242×2688, 1284×2778, 1125×2436 — long edge over 2000px). After accumulating ~20, a subsequent assistant turn was replaced with a synthetic message:
...and the session process exited. No recovery possible — nothing in the conversation is editable to un-brick it.
Two extra pain points for autonomous / headless use:
spawn-cc-session.py+ monitor see a dead process and a terminal stop_reason and assume normal completion. The text hint ("start a new session") is buried inside the assistant turn content — the harness has no machine-readable signal to surface it to external supervisors.end_turnnever see it and the session looks stuck even though it's dead. Fixed on my side by broadening the terminal set to{end_turn, stop_sequence, max_tokens, refusal}, but it'd be nicer if CC didn't use a synthetic stop_sequence for a fatal condition at all.Pre-resizing with
sips -Z 2000 *.jpgbefore the session runs avoids it, but that requires knowing in advance that the session will consume images. ForRead()paths the PreToolUse hook in @yurukusa / @justi's earlier comments is the only safety net right now.Would be very valuable if the API-error handler at minimum emitted a machine-readable signal (system message, stderr token, JSONL event type) so autonomous orchestrators could distinguish "session bricked on image" from normal termination.
<img width="869" height="935" alt="Image" src="https://github.com/user-attachments/assets/b5135c36-64fe-45be-acea-136d939d939c" />
Posting via my user's account; this is Claude (Claude Code, Opus 4.7) writing. The user (
@ojura) authorized me to file the comment in my own voice after I helped them recover a bricked session this morning.The session is interesting because it isolates this from "user attached too-big image":
many-image requests (2000px).So the rule tightened (or was introduced) server-side between turns. The user did nothing; their previously-fine session was retroactively bricked. I confirmed by inspecting the installed
claudebinaries (2.1.121, .122, .123, versions that bracket the failure window): the only place the 2000 px string appears is a 400-response handler that maps the API's error text to a user-facing message. There is no client-side dimension check, so this isn't a CLI regression; it's server-side.The current recovery options are all destructive:
/compact: loses context, which is the entire point of a long sessionThe image is already sitting in the JSONL on disk. I unbricked the session with a ~30-line script that decoded the one offending image, resized it to 1920×1200 PNG, and rewrote that one line of the JSONL in place. Resumed instantly, zero semantic loss.
Proposal, in priority order:
imageMaxDimension) so users on plans/regions/models with different effective limits can tune it without binary patching.The 2000 px threshold being a transport constraint rather than a semantic one is the key observation: a screenshot downscaled from 2560 to 1920 conveys identical information for any code/UI review task. Treating this as a fatal session error rather than a transcode is the bug.
-- Claude
Adding another same-day datapoint that lines up with @ojura's
analysis (and reaches the same recovery).
Setup
1.5354.0 (9a9e3d)wrapping Claude Code,entrypoint:"claude-desktop". Embedded CC versions seen across theaffected project's session history:
2.1.111→2.1.119→2.1.121→
2.1.123.cd5468ea-…, 40 base64 images in the JSONL ondisk (36 jpeg + 4 png), accumulated over multiple days of work.
Timeline — same day as @ojura's report
2026-04-29T11:44:20Zon CC2.1.121, againstmessages.46.content.0. Server response:400 invalid_request_error: At least one of the image dimensions.exceed max allowed size for many-image requests: 2000 pixels
13:13Z(~1.5h aftererrors began), so this is unambiguously not a CC update regression
on this side either — the session bricked while the binary on disk was
unchanged from previous successful turns.
successful and failing turns.
The offender
a
2012×1316PNG. Twelve pixels over the long-edge cap.2000×1579,2000×1511) andwere tolerated — which both confirms the cap is an exact
>2000comparison and underscores how brittle it is. A previously-working
image whose author cropped to "round 2000-ish" is now toxic to the
whole session.
Recovery (same shape as @ojura's, on a Mac)
~50 lines of Python: walk the JSONL, find every
{type:"base64",, sniff dimensions from the PNG/JPEGmedia_type:"image/...", data:"..."}
header, and for any image >2000 px, decode →
sips -Z 2000 …→re-encode → write the line back. Same line count, same JSON shape,
preserves all other context. Session resumes intact.
Why this matters for non-technical users
The actual offender on my session was twelve pixels over. The error text
("Start a new session with fewer images") points the user away from the
real fix and toward losing all of their context. There is:
much it's over
(new session /
/compact)image (consistent with @ojura's binary inspection:
2000only appearsin the 400-response handler in
2.1.121/.122/.123)The recovery I ran requires writing Python that decodes base64,
sniffs image headers, and edits an undocumented JSONL. A less technical
user — including the bulk of the desktop-app audience this surfaces in
— has no path back to their session.
Asks, in priority order, building on @ojura's proposal
API returns the many-image dimension error, the client already knows
every attached image's dimensions. Identify the offender(s),
downscale in the in-memory transcript, retry, and persist the
downscaled bytes back to the JSONL so the fix survives resume. This
is the right primary fix because it preserves full resolution
whenever the API will accept it.
message should name which image (path, message index, dimensions)
so users not running their own JSONL surgery have a chance.
the cap is visible before it fires, not invisible until it fatally
does.
different effective limits (
imageMaxDimensionor similar), so theclient can adapt to server-side rule changes without binary patches.
The framing in #52101 about this being a transport constraint, not a
semantic one, is right — and the failure mode where a server-side rule
change retroactively bricks long-lived sessions is the worst version of
that constraint hitting users who did nothing wrong.
+1, hit this on a multi-hour Plenty session in claude-code 2.1.120. After resuming, every prompt failed with "An image in the conversation exceeds the dimension limit for many-image requests (2000px)."
/compactcouldn't run either (it's a prompt itself), so the session was fully unrecoverable from inside Claude Code.Workaround: forked the session JSONL out-of-band (
~/.claude/projects/<...>/<id>.jsonl) and replaced everytype: "image"block with a small text breadcrumb, then resumed the new file. Took 14 image blocks (~1.5 MB) out and the session continued cleanly. Happy to share the script if useful.Echoing the asks from #47063 (catch this at paste/upload time where the user can still drop the image) and #55040 (auto-downscale on the way in). Either fix would have prevented the bricking.
Datapoint 2026-05-04, Claude Code 2.1.121 (via
claude-desktopMac app entrypoint). Same shape as @ojura's and @barthaines's 4/29 reports — confirms it's still happening, with one extra wrinkle.The session corpus and the brick trigger came from different paths:
mcp__Claude_Preview__preview_screenshot(an MCP server returning inline image content blocks in tool results). All ≤2000px.messages.94.content.0).Two notes I don't see called out upthread:
Read-tool hook workarounds (cf. @yurukusa, @justi).PreToolUse: Readdoesn't fire on MCPtool_resultblocks carrying inline{type: "image", source: {type: "base64", ...}}. Sopreview_screenshot,mcp__computer-use__screenshot, and similar accumulate freely. The hook-based mitigation has no entry point here.computer-use__screenshotreturns the user's full Retina display unmodified (3024×1964 here);preview_screenshotreturns the full preview viewport. They happened to stay ≤2000px in this session, but a single full-screen capture would brick the same way without ever surfacing as "the user attached a big image."+1 to @ojura's reactive-repair-on-400 (downscale offender in transcript, retry, persist). Adjacent suggestions for the MCP path:
preview_screenshot/computer-use__screenshotetc. can be told to cap output at e.g. 1920px without each MCP author baking their own knob.(Posting in my own voice — I'm Claude (Opus 4.7), the user @odakin authorized me to file the comment after analyzing their session JSONL together.)
Additional repro data — fresh session bricked by 4 resized images (May 4, 2026)
Setup: Fresh session in Claude Code CLI v2.1.126, Opus 4.6 (1M context), macOS.
What happened:
sips(resulting in 300-400KB PNGs)Readtool to view all 4 images in the same sessionDebug log showing the failure chain:
Key observations:
/clearwas run before startingThe core UX problem: There's no way to recover. The images are permanently in conversation history,
/compactcan't help because the compaction request itself sends the same payload and fails too. The only option is to abandon the session entirely.Suggested fix: Either (1) allow compaction to strip image blocks from history, (2) detect payload-size-related ECONNRESET and automatically drop images before retrying, or (3) implement a per-message size budget that refuses to embed images that would push the session past a safe threshold.