claude-code - 💡(How to fix) Fix [BUG] Oversized-image 400 error triggers a retry loop that invalidates prompt cache and inflates cost ~35× [1 pull requests]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

Error Message

API Error: an image in the conversation could not be processed and was removed. Double press esc to edit your message, or re-read the file if you still need it.

Underlying API error

400 {"type":"error","error":{"type":"invalid_request_error", "message":"messages.51.content.20.image.source.base64.data: At least one of the image dimensions exceed max allowed size for many-image requests: 2000 pixels"}, "request_id":"req_..."}

  • If it can't be sent, the agent should stop and surface the error once instead of entering a retry loop, and | Before first image error | 254 | ~12.9k | ~252.8k | | After first image error | 41 | ~452.2k (35×) | ~109.7k | Cost impact: the 41 post-error calls (14% of all calls) cost ≈ $120 of the ≈ $182 session (~66%). The broken-image loop more than doubled the session cost. (Opus-tier pricing: cache write $6.25 / 1M vs cache read $0.50 / 1M — a ~12× per-token penalty for every re-cached turn.)
  • When history is edited mid-session, warn that the prompt cache will be invalidated, and/or restructure edits to preserve the cacheable prefix.
  • If it can't be sent, the agent should stop and surface the error once instead of entering a retry loop, and

Error Messages/Logs

400 {"type":"error","error":{"type":"invalid_request_error", "message":"messages.51.content.20.image.source.base64.data: At least one of the image dimensions exceed max allowed size for many-image requests: 2000 pixels"}, "request_id":"req_..."}

  • If it can't be sent, the agent should stop and surface the error once instead of entering a retry loop, and

Root Cause

Steps to reproduce

  1. In a long-running session, have the agent read/attach an image larger than 2000px in some dimension (e.g. a generated plot / screenshot).
  2. The API rejects the request with the 400 above.
  3. Observe Claude Code remove the image and continue issuing requests.
  4. Inspect token usage in the session .jsonl (or ccusage).

Expected behavior

  • The oversized image should be downscaled/resized client-side to fit the limit before sending, or
  • If it can't be sent, the agent should stop and surface the error once instead of entering a retry loop, and
  • Removing content from history should not silently nuke the prompt cache for the rest of the session without warning.

Actual behavior

  • The image is stripped and the agent retries 41 times.
  • Prompt caching breaks: cache read drops to a constant ~9.7k tokens while cache write balloons and climbs every turn.

Evidence (single session, 295 API calls total)

PhaseCallsAvg cache write/callAvg cache read/call
Before first image error254~12.9k~252.8k
After first image error41~452.2k (35×)~109.7k
Per-call cache-write after caching fully broke (call #263 onward) grew monotonically:
524k → 531k → 538k → … → 593k tokens — the whole context re-cached every turn.
Cost impact: the 41 post-error calls (14% of all calls) cost ≈ $120 of the ≈ $182 session (~66%). The broken-image loop more than doubled the session cost. (Opus-tier pricing: cache write $6.25 / 1M vs cache read $0.50 / 1M — a ~12× per-token penalty for every re-cached turn.)

Root cause (analysis)

  1. Image exceeds the 2000px many-image limit → API 400.
  2. Claude Code mutates the message history to remove the image.
  3. The cached prefix no longer matches → cache miss for the whole prefix.
  4. Every subsequent turn re-writes the full (and growing) context at the cache-write rate instead of reusing it at the cache-read rate.
  5. The agent keeps retrying, so the penalty repeats and compounds.

Suggested fixes

  • Pre-validate & auto-resize images to the API's max dimensions (and per-image / many-image rules) before sending.
  • Don't enter an unbounded retry loop on invalid_request_error for images — fail fast and ask the user once.
  • When history is edited mid-session, warn that the prompt cache will be invalidated, and/or restructure edits to preserve the cacheable prefix.
  • Optionally surface a cost/cache-miss warning when cache-read drops to ~0 across many consecutive turns.

Notes

Detected by parsing the session transcript and summing per-request usage (deduped by requestId); cache-write tokens ar

Fix Action

Fixed

Code Example

400 {"type":"error","error":{"type":"invalid_request_error", "message":"messages.51.content.20.image.source.base64.data: At least one of the image dimensions exceed max allowed size for many-image requests: 2000 pixels"}, "request_id":"req_..."}
RAW_BUFFERClick to expand / collapse

Preflight Checklist

  • I have searched existing issues and this hasn't been reported yet
  • This is a single bug report (please file separate reports for different bugs)
  • I am using the latest version of Claude Code

What's Wrong?

When a request contains an image whose dimensions exceed the API limit (2000px for many-image requests), the API returns a 400 invalid_request_error. Claude Code surfaces this as:

API Error: an image in the conversation could not be processed and was removed. Double press esc to edit your message, or re-read the file if you still need it.

and then silently removes the image and keeps retrying. Each retry mutates the conversation prefix, which invalidates the prompt cache. From that point on, cache reads collapse and the entire context is re-written to the cache on every turn, causing cache-write tokens (and cost) to explode. In my session this single loop accounted for ~66% of the total session cost.

Environment

  • Claude Code: 2.1.1592.1.160 (background/cli session)
  • Model: claude-opus-4-8
  • OS: Linux (WSL2, Ubuntu 24.04)
  • Permission mode: bypassPermissions

Underlying API error

400 {"type":"error","error":{"type":"invalid_request_error", "message":"messages.51.content.20.image.source.base64.data: At least one of the image dimensions exceed max allowed size for many-image requests: 2000 pixels"}, "request_id":"req_..."}

Steps to reproduce

  1. In a long-running session, have the agent read/attach an image larger than 2000px in some dimension (e.g. a generated plot / screenshot).
  2. The API rejects the request with the 400 above.
  3. Observe Claude Code remove the image and continue issuing requests.
  4. Inspect token usage in the session .jsonl (or ccusage).

Expected behavior

  • The oversized image should be downscaled/resized client-side to fit the limit before sending, or
  • If it can't be sent, the agent should stop and surface the error once instead of entering a retry loop, and
  • Removing content from history should not silently nuke the prompt cache for the rest of the session without warning.

Actual behavior

  • The image is stripped and the agent retries 41 times.
  • Prompt caching breaks: cache read drops to a constant ~9.7k tokens while cache write balloons and climbs every turn.

Evidence (single session, 295 API calls total)

PhaseCallsAvg cache write/callAvg cache read/call
Before first image error254~12.9k~252.8k
After first image error41~452.2k (35×)~109.7k
Per-call cache-write after caching fully broke (call #263 onward) grew monotonically:
524k → 531k → 538k → … → 593k tokens — the whole context re-cached every turn.
Cost impact: the 41 post-error calls (14% of all calls) cost ≈ $120 of the ≈ $182 session (~66%). The broken-image loop more than doubled the session cost. (Opus-tier pricing: cache write $6.25 / 1M vs cache read $0.50 / 1M — a ~12× per-token penalty for every re-cached turn.)

Root cause (analysis)

  1. Image exceeds the 2000px many-image limit → API 400.
  2. Claude Code mutates the message history to remove the image.
  3. The cached prefix no longer matches → cache miss for the whole prefix.
  4. Every subsequent turn re-writes the full (and growing) context at the cache-write rate instead of reusing it at the cache-read rate.
  5. The agent keeps retrying, so the penalty repeats and compounds.

Suggested fixes

  • Pre-validate & auto-resize images to the API's max dimensions (and per-image / many-image rules) before sending.
  • Don't enter an unbounded retry loop on invalid_request_error for images — fail fast and ask the user once.
  • When history is edited mid-session, warn that the prompt cache will be invalidated, and/or restructure edits to preserve the cacheable prefix.
  • Optionally surface a cost/cache-miss warning when cache-read drops to ~0 across many consecutive turns.

Notes

Detected by parsing the session transcript and summing per-request usage (deduped by requestId); cache-write tokens ar

What Should Happen?

Expected behavior

  • The oversized image should be downscaled/resized client-side to fit the limit before sending, or
  • If it can't be sent, the agent should stop and surface the error once instead of entering a retry loop, and
  • Removing content from history should not silently nuke the prompt cache for the rest of the session without warning.

Error Messages/Logs

400 {"type":"error","error":{"type":"invalid_request_error", "message":"messages.51.content.20.image.source.base64.data: At least one of the image dimensions exceed max allowed size for many-image requests: 2000 pixels"}, "request_id":"req_..."}

Steps to Reproduce

  1. In a long-running session, have the agent read/attach an image larger than 2000px in some dimension (e.g. a generated plot / screenshot).
  2. The API rejects the request with the 400 above.
  3. Observe Claude Code remove the image and continue issuing requests.
  4. Inspect token usage in the session .jsonl (or ccusage).

Expected behavior

  • The oversized image should be downscaled/resized client-side to fit the limit before sending, or
  • If it can't be sent, the agent should stop and surface the error once instead of entering a retry loop, and
  • Removing content from history should not silently nuke the prompt cache for the rest of the session without warning.

Claude Model

Opus

Is this a regression?

I don't know

Last Working Version

No response

Claude Code Version

2.1.160

Platform

Anthropic API

Operating System

Linux

Terminal/Shell

Bash

Additional Information

No response

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

FAQ

Expected behavior

  • The oversized image should be downscaled/resized client-side to fit the limit before sending, or
  • If it can't be sent, the agent should stop and surface the error once instead of entering a retry loop, and
  • Removing content from history should not silently nuke the prompt cache for the rest of the session without warning.

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING