Image processing failure silently passes oversized images into context, bricking sessions

Status Fixed / completed
Reported on v2.1.76
Maintainer reply None cached
Activity 13 comments · opened Mar 15, 2026 · closed Aug 19, 2026

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

  1. Start a Claude Code session on a system where the native image processor may fail (or simulate a failure)
  2. Accumulate several images in the conversation through screenshots or the Read tool
  3. Include an image that exceeds 2000px in any dimension where the resize fails (e.g., unsupported format)
  4. The raw oversized image enters context silently
  5. The API returns the 400 "many-image" dimension error
  6. 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

View original on GitHub ↗

12 Comments

github-actions[bot] · 5 months ago

Found 3 possible duplicate issues:

  1. https://github.com/anthropics/claude-code/issues/13480
  2. https://github.com/anthropics/claude-code/issues/34025
  3. https://github.com/anthropics/claude-code/issues/2939

This issue will be automatically closed as a duplicate in 3 days.

  • If your issue is a duplicate, please close it and 👍 the existing issue instead
  • To prevent auto-closure, add a comment or 👎 this comment

🤖 Generated with Claude Code

yurukusa · 5 months ago

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

{
  "hooks": {
    "PreToolUse": [{
      "matcher": "Read",
      "hooks": [{
        "type": "command",
        "command": "bash ~/.claude/hooks/image-size-guard.sh"
      }]
    }]
  }
}

~/.claude/hooks/image-size-guard.sh:

#!/usr/bin/env bash
# Block Read tool from loading images that exceed safe dimensions
INPUT=$(cat)
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
[[ -z "$FILE" ]] && exit 0

# Only check image files
case "${FILE,,}" in
  *.png|*.jpg|*.jpeg|*.gif|*.webp|*.bmp|*.tiff) ;;
  *) exit 0 ;;
esac

[[ ! -f "$FILE" ]] && exit 0

# Check dimensions with identify (ImageMagick) or file
if command -v identify &>/dev/null; then
  DIMS=$(identify -format '%wx%h' "$FILE" 2>/dev/null | head -1)
  W=${DIMS%x*}; H=${DIMS#*x}
  if [[ "$W" -gt 2000 || "$H" -gt 2000 ]]; then
    echo "BLOCKED: Image ${W}x${H} exceeds 2000px limit. Resize first: convert \"$FILE\" -resize 2000x2000\\> \"$FILE\"" >&2
    exit 2
  fi
fi

# Fallback: check file size (>10MB is suspicious for context)
SIZE=$(stat -f%z "$FILE" 2>/dev/null || stat -c%s "$FILE" 2>/dev/null)
if [[ "$SIZE" -gt 10485760 ]]; then
  echo "BLOCKED: Image is ${SIZE} bytes (>10MB). Likely too large for context." >&2
  exit 2
fi

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, /compact is the only escape, and even that doesn't always work if the API error persists across compaction boundaries.

justi · 5 months ago

Workaround that addresses this for the Read tool path: a PreToolUse hook 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 Read tool. Images pasted/dragged into the terminal or injected via data:image URLs 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:

  • Image accumulation — even images under 2000px accumulate in context and trigger the same 400 error after ~8 reads. The proxy mode keeps zero image data in context, so there's no accumulation at all.
  • Transparent PNGs — images with alpha channels crash the API regardless of dimensions. The hook always converts to flattened JPEG.
  • Unusual encodings — Selenium screenshots, RGBA PNGs, some macOS native screenshots fail even at small sizes. Conversion normalizes them.

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.

ianbmacdonald · 5 months ago

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" />

mattbvb · 4 months ago

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.

TweedBeetle · 4 months ago

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:

model: "<synthetic>"
stop_reason: "stop_sequence"
text: "An image in the conversation exceeds the dimension limit for
       many-image requests (2000px). Start a new session with fewer images."

...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:

  1. Silent for spawners. The synthesized stop happens mid-task. Orchestrators running 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.
  1. stop_reason=stop_sequence confuses wait-for-stop logic. Downstream monitors that watch for end_turn never 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 *.jpg before the session runs avoids it, but that requires knowing in advance that the session will consume images. For Read() 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.

JPMasters-AUS · 4 months ago
Adding a user's perspective to this thread — I'm not a developer, so apologies if this has already been covered. I've been hitting the same error repeatedly: > "An image in the conversation exceeds the dimension limit for many-image requests (2000px). Start a new session with fewer images." The confusing part is that in the third attempt, I did not attach an image. As far as I can tell, Claude Code itself is pulling images into the conversation in the background (I’m told this can happen via tool results, IDE integrations, or MCP servers), and then the session becomes unusable until I start over. From a user’s point of view, two things would help enormously: 1. A clearer error that says which image is the problem and where it came from, so I know what to avoid next time. 2. An option to automatically downscale or drop oversized images before they're sent, rather than having to abandon the session. Right now, the only workaround I've found is to start a New session, which loses all the context I'd built up.
>In the attached screenshot, you can see that I tried to upload an image on the first two attempts, and on the third attempt, I did not upload an image — but I still received the same error.

<img width="869" height="935" alt="Image" src="https://github.com/user-attachments/assets/b5135c36-64fe-45be-acea-136d939d939c" />

ojura · 4 months ago

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":

  • Session had 91 images, biggest a 2560×1600 PNG attached early.
  • Last successful turn: 2026-04-26 21:50.
  • Next user turn: 2026-04-29 07:18: no images added, no edits to history, payload to the API essentially identical to the last successful one.
  • That next turn failed with 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 claude binaries (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 session
  • "Start a new session": same

The 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:

  1. Reactive repair on 400. When the API returns the many-image error, the CLI knows the dimensions of every attached image. Identify the offender(s), downscale in the in-memory transcript, retry. No session loss. Optionally persist the downscaled version back to the JSONL so the fix survives resume. This is the highest-leverage fix because it preserves full resolution whenever the API will accept it; the rule depends on image count and possibly server-side policy, so capping proactively throws away resolution the API would have taken.
  2. Proactive resize at attach time as a fallback / convenience knob, for users who'd rather pay the resolution cost up front than risk a stutter mid-turn.
  3. Make the cap configurable (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

barthaines · 4 months ago

Adding another same-day datapoint that lines up with @ojura's
analysis (and reaches the same recovery).

Setup

  • Claude for Mac desktop app 1.5354.0 (9a9e3d) wrapping Claude Code,

entrypoint:"claude-desktop". Embedded CC versions seen across the
affected project's session history: 2.1.1112.1.1192.1.121
2.1.123.

  • Long-running session cd5468ea-…, 40 base64 images in the JSONL on

disk (36 jpeg + 4 png), accumulated over multiple days of work.

Timeline — same day as @ojura's report

  • Errors fired starting 2026-04-29T11:44:20Z on CC 2.1.121, against

messages.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
.

  • The 2.1.123 binary wasn't placed on disk until 13:13Z (~1.5h after

errors 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.

  • Same JSONL on disk pre- and post-error. No user edits between

successful and failing turns.

The offender

  • Of 40 images in the JSONL, exactly one exceeded 2000 px on any side:

a 2012×1316 PNG. Twelve pixels over the long-edge cap.

  • Other images sat right at the limit (2000×1579, 2000×1511) and

were tolerated — which both confirms the cap is an exact >2000
comparison 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",
media_type:"image/...", data:"..."}
, sniff dimensions from the PNG/JPEG
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:

  • no preflight surfacing of which image, where it came from, or by how

much it's over

  • no in-place edit recovery — the only documented path is destructive

(new session / /compact)

  • no client-side resize on the data path that produced the bricking

image (consistent with @ojura's binary inspection: 2000 only appears
in 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

  1. Reactive repair on 400 from this specific error class. When the

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.

  1. Identify-the-offender error text. At minimum, the user-facing

message should name which image (path, message index, dimensions)
so users not running their own JSONL surgery have a chance.

  1. Surface in-context image dimensions in the context indicator so

the cap is visible before it fires, not invisible until it fatally
does.

  1. Make the cap configurable for users on plans/regions/models with

different effective limits (imageMaxDimension or similar), so the
client 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.

edo-ceder · 4 months ago

+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)." /compact couldn'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 every type: "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.

odakin · 3 months ago

Datapoint 2026-05-04, Claude Code 2.1.121 (via claude-desktop Mac 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:

  • 89 image content blocks total in the JSONL (1498 lines, 5.3 MB).
  • 44 of them produced by mcp__Claude_Preview__preview_screenshot (an MCP server returning inline image content blocks in tool results). All ≤2000px.
  • Exactly one image exceeded the cap: a user-pasted 2400×1080 JPEG (~120 KB), 400 px over the long-edge limit. This was the offender (messages.94.content.0).
  • First failing turn included that image; the next turn — pure text, no new image — failed identically because the offender stays in history. Confirmed unrecoverable.

Two notes I don't see called out upthread:

  1. MCP-tool-result images bypass the Read-tool hook workarounds (cf. @yurukusa, @justi). PreToolUse: Read doesn't fire on MCP tool_result blocks carrying inline {type: "image", source: {type: "base64", ...}}. So preview_screenshot, mcp__computer-use__screenshot, and similar accumulate freely. The hook-based mitigation has no entry point here.
  2. **Retina screenshots from these MCPs are latently over-cap.** computer-use__screenshot returns the user's full Retina display unmodified (3024×1964 here); preview_screenshot returns 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:

  • Client-side downscale at the MCP-image-result boundary, mirroring whatever path user-attached images take. Right now the per-image cap is enforced 0% client-side and 100% server-side, so any MCP that returns a Retina screenshot is a future brick.
  • Per-tool image-dimension cap config so preview_screenshot / computer-use__screenshot etc. 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.)

neokry · 3 months ago

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:

  1. Had 4 concept art PNGs (~1.6-2.7MB originals) generated by an external tool
  2. Resized them to thumbnails using sips (resulting in 300-400KB PNGs)
  3. Used the Read tool to view all 4 images in the same session
  4. Each image encoded to ~125-145KB of base64 in conversation history
  5. Total session payload: ~1.1MB, with images accounting for ~1MB (95%)
  6. The API request immediately after reading all 4 images hit ECONNRESET
  7. All 11 retry attempts failed with "Connection error" / ECONNRESET
  8. Session permanently bricked — cannot recover

Debug log showing the failure chain:

19:55:56 [API REQUEST] /v1/messages source=repl_main_thread
19:56:26 [WARN] Slow first byte: no stream chunk 30.0s after request sent (attempt 1)
19:57:27 [ERROR] API error (attempt 1/11): undefined Connection error.
...repeats through attempt 11...
20:07:42 [ERROR] API error (attempt 11/11): undefined Connection error.
20:07:42 [ERROR] Connection error details: code=ECONNRESET, message=The socket connection was closed unexpectedly.

Key observations:

  • This was a fresh session (not resumed) — /clear was run before starting
  • The images were already resized to thumbnails (300-400KB PNGs, not the multi-MB originals)
  • ~500KB total of raw image data across 4 images is not unreasonable
  • A separate session running simultaneously in the same project (without images) worked fine
  • Anthropic status page showed multiple API incidents earlier on May 4, but marked as resolved at the time this occurred

The core UX problem: There's no way to recover. The images are permanently in conversation history, /compact can'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.

Showing cached comments. Read the full discussion on GitHub ↗