Corrupted image in context causes persistent API 400 errors, breaking the entire chat session

Resolved 💬 23 comments Opened Jan 18, 2026 by erold90 Closed May 19, 2026
💡 Likely answer: A maintainer (claude[bot], contributor) responded on this thread — see the highlighted reply below.

Bug Description

When Claude Code reads a corrupted/invalid image file (e.g., a .jpg file that actually contains HTML text), the invalid image data remains in the conversation context and causes persistent API 400 errors ("Could not process image") for all subsequent messages, making the entire chat session unusable.

Steps to Reproduce

  1. Have a file with .jpg extension that contains invalid data (e.g., <html><body>404</body></html> - a 404 error page saved as .jpg)
  2. Ask Claude Code to read/analyze this image file
  3. Claude attempts to read it, receives API error 400
  4. Every subsequent message in that chat session fails with the same error, even unrelated messages

Example

⏺ Read(path/to/corrupted.jpg)
  ⎿  Read image (29 bytes)
  ⎿  API Error: 400 {"type":"error","error":{"type":"invalid_request_error","message":"Could not process image"}}

❯ hello  (completely unrelated message)
  ⎿  API Error: 400 {"type":"error","error":{"type":"invalid_request_error","message":"Could not process image"}}

Expected Behavior

  • The error should be handled gracefully without corrupting the conversation context
  • Invalid image data should be validated/sanitized before being added to the context
  • Or at minimum, the user should be able to continue the conversation after an image processing error

Actual Behavior

The chat session becomes completely unusable. The only workaround is to start a new session, losing all conversation history.

Environment

  • Claude Code CLI
  • macOS (Darwin 24.6.0)

Additional Context

The corrupted file in this case was 29 bytes containing <html><body>404</body></html> - likely a failed download that saved the error page instead of the actual image. This is a common scenario when downloading images from URLs that no longer exist.

View original on GitHub ↗

23 Comments

github-actions[bot] · 5 months ago

Found 3 possible duplicate issues:

  1. https://github.com/anthropics/claude-code/issues/4083
  2. https://github.com/anthropics/claude-code/issues/18528
  3. https://github.com/anthropics/claude-code/issues/17452

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

muriloime · 5 months ago

same here. each time I paste an image to Claude code, on linux (pop-os 24, wayland)

NathanR-85 · 5 months ago
xj-zh · 5 months ago

Same issue here. In my case, downloading a GitHub issue attachment (user-attachments URL) resulted in an empty/invalid image, which then poisoned the entire conversation context.

pellepelle3 · 4 months ago

i was able to surgically remove the tool call and result from the jsonl for that and compact and continue using it.

GhostPeony · 4 months ago

I'm running into this error as well with a specific PNG that was taken on a Nintendo 2DS and sent to the Claude API via an HTTP bridge from the Nintendo device. The PNG was corrupted and the image was half sized.

I got 400 Errors hitting the Claude API, and when I had Claude Code investigating my bridge between the Nintendo Device and analyzing the image to find out went wrong. Claude Code started throwing 400 errors, and even closing out and resuming the session did not get rid of the errors, even when not searching for the image.

The fact that the image had been in the agent's context, it was unusable. I verified this again with an API call and I was also able to email the image via gmail, to an inbox I had a claude agent monitoring, and it also crashed the agent with 400 errors.

Wanting to raise awareness as I can see a corrupted PNG being used to crash API agents maliciously via emails and other delivery methods.

tfvchow · 4 months ago
Note: This root cause analysis was done collaboratively with Claude Code (Opus 4.6). If that bothers you, feel free to scroll past. Nobody's getting paid here — let alone using my own subscription to troubleshoot for Anthropic. Just sharing what I found in case it helps.

Root Cause (v2.1.76)

The image reading pipeline has no bail-out for non-image content. Three functions form the chain:

1. pF6 (magic byte detection) — defaults to "image/png" when bytes don't match any known format, instead of returning null:

// after checking PNG, JPEG, GIF, WebP magic bytes:
return "image/png";  // ← HTML, error text, anything unknown = "PNG"

2. XV8 (Read tool image handler) — when sharp correctly rejects the content ("unsupported image format"), the catch block wraps the raw bytes as PNG anyway:

try {
  O = await Bk(Y, z, w);    // sharp rejects
} catch(H) {
  O = bP1(Y, w, z);         // fallback: base64-encode raw bytes, label as PNG, send to API
}

3. rf4 (paste-time handler) — calls sharp with no catch block at all, crashing the input handler when a bad image propagates via copy-paste.

Minimal Patch

Tested on a stock v2.1.76 devcontainer (no MCP, no custom config). Apply to cli.js:

# python3 patch.py $(which claude | xargs readlink -f | xargs dirname)/cli.js
import sys
with open(sys.argv[1], 'r') as f: content = f.read()

# 1. pF6: return null for unknown magic bytes
content = content.replace(
    'return"image/webp"}return"image/png"}',
    'return"image/webp"}return null}', 1)

# 2. XV8: bail out when pF6 returns null
content = content.replace(
    'let _=pF6(Y),w=_.split("/")',
    'let _=pF6(Y);if(_===null)return{type:"text",file:{filePath:A,content:"[Not a valid image file]",numLines:0,startLine:1,totalLines:0}};let w=_.split("/")', 1)

# 3. rf4: skip non-images before sharp
content = content.replace(
    'let z=D8Y(K).slice(1).toLowerCase()||"png",_=await Bk(Y,Y.length,z)',
    'let z=D8Y(K).slice(1).toLowerCase()||"png";if(pF6(Y)===null)return null;let _=await Bk(Y,Y.length,z)', 1)

with open(sys.argv[1], 'w') as f:
    f.write(content)

Breaks on next npm update — meant as a stopgap, not a permanent solution.

Full write-up with both vectors (Read tool + paste propagation): https://github.com/tfvchow/field-notes-public/issues/55

justi · 3 months ago

Update: The workaround hook now uses a subprocess proxy (v9) — instead of passing converted images to the context (which still accumulates), it spawns a fresh claude --model haiku to analyze each image and returns text only. Zero image data in context, unlimited reads per session.

Features: auto-context from session transcript, PostToolUse cleanup, prompt injection resistant.

Gist: https://gist.github.com/justi/8265b84e70e8204a8e01dc9f99b8f1d0

Stress-tested: 15 images in one session, zero errors.

---
cc @erold90 @GhostPeony @NathanR-85 @muriloime @pellepelle3 @tfvchow @xj-zh

tea-artist · 3 months ago

Same here, v2.1.92. In our case, an ICO file named .png was read by the Read tool — Claude Code inferred media_type: image/png from the extension, but the actual content was ICO format (00 00 01 00 magic bytes). The API rejected it with Image format image/png not supported, and the conversation became permanently broken since the bad image block replays on every subsequent message.

kutzki · 2 months ago

Having a similar issue

justi · 2 months ago

@tea-artist — the ICO-as-PNG case you described is exactly what this hook mitigates. The subprocess proxy intercepts every image Read before it hits the main session, so invalid or mismatched formats fail inside a sandboxed claude --model haiku call and return text only — no broken image bytes ever reach the primary context, no thinking-block corruption, session stays alive.

Gist (already linked higher in this thread): https://gist.github.com/justi/8265b84e70e8204a8e01dc9f99b8f1d0

fsc-eriker · 2 months ago

Adding a noise comment to prevent this from being closed.

junaidtitan · 2 months ago

Corrupted images poisoning the entire session with persistent 400 errors is exactly what cozempic's image-strip strategy handles — it removes base64 image blocks from the JSONL so the toxic data stops being re-sent every turn. The doctor check can also detect this pattern. pip install cozempic https://github.com/Ruya-AI/cozempic — let me know if it helps.

uditgoenka · 2 months ago

I am having the same issue

WellnessCoding · 2 months ago

same issue here

zmtno · 2 months ago

same issue

charlyfdz · 2 months ago

same here, not woking after the API ERROR appears

eried · 2 months ago

same issue here

maverick193 · 2 months ago

Same issue on Claude code v2.1.139 (debian 13)

junaidtitan · 2 months ago

The corrupted image staying in context and poisoning every subsequent request is really frustrating. Cozempic (https://github.com/Ruya-AI/cozempic) has an image-strip strategy that removes all inline image blocks from the JSONL, plus a claude-json-corruption doctor check that flags malformed entries. Running cozempic treat --rx standard on the affected session should clear the bad image data and make the session usable again. Curious if anyone here has tried it.

junaidtitan · 2 months ago

Once a corrupted image lands in your session JSONL, every subsequent turn replays it and hits the same 400. Cozempic's image-strip strategy removes image content blocks from older conversation turns, which would clear the corrupted entry. The doctor command also flags corrupted session state. pip install cozempic && cozempic treat on the affected session should make it usable again. Repo: https://github.com/Ruya-AI/cozempic — happy to hear if it resolves the loop for you.

claude[bot] contributor · 1 month ago

This is a duplicate of #47976, which was fixed as of version 2.1.144.

github-actions[bot] · 8 days ago

This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.