[BUG] Oversized-image 400 error triggers a retry loop that invalidates prompt cache and inflates cost ~35×

Resolved 💬 5 comments Opened Jun 5, 2026 by PeterGanZW Closed Jul 11, 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 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)

| Phase | Calls | Avg cache write/call | Avg cache read/call |
|---|---|---|---|
| Before first image error | 254 | ~12.9k | ~252.8k |
| After first image error | 41 | ~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_

View original on GitHub ↗

This issue has 5 comments on GitHub. Read the full discussion on GitHub ↗