Read tool returns incorrect image content for PNG files (rendering routed to wrong file / permuted across batch)
Claude Code: Read tool returns wrong image content for PNG paths
Date observed: 2026-04-24
Claude Code version: 2.1.118 (native, win32-x64)
Model: claude-opus-4-7 (1M context), via Bedrock auth
Severity: Correctness bug in multimodal Read pipeline. The model receives image content that does not match the file's actual pixel data.
Summary
When the Read tool is called on a PNG file path, the rendered image content presented to the model does not match the file's pixel data. Across multiple Read calls on the same paths within one session, returned image content flips between mismatched files, indicating the rendering or routing layer serves wrong content. Minimal reproduction with tiny synthetic PNGs (under 1.5 KB each) confirms the bug is not size-dependent, not model-interpretation, and not caused by image content complexity.
Minimal Reproduction
A script generated 5 test PNGs with known content (via Python and PIL), saved to a local temp directory.
| File | SHA-256 (first 16 hex) | Size | Dims | Known content |
|---|---|---|---|---|
| test1_solid_red_100x100.png | 1dc0ddcbc8ec8770 | 284 B | 100x100 | All pixels RGB (255, 0, 0) |
| test2_solid_white_100x100.png | 996ada7513a97066 | 286 B | 100x100 | All pixels RGB (255, 255, 255) |
| test3_solid_blue_100x100.png | 8fd808817157f21a | 285 B | 100x100 | All pixels RGB (0, 0, 255) |
| test4_top_green_bottom_black_200x200.png | 7aadb22946d88d1d | 396 B | 200x200 | Top half RGB (0,255,0), bottom half (0,0,0) |
| test5_4quadrants_400x400.png | e1ffdf127bb1e5e5 | 1,403 B | 400x400 | Quadrants: TL red, TR green, BL blue, BR yellow |
Test 1: batch Read of all 5 files in a single assistant message
| Call order | File sent | Rendered content received by model |
|---|---|---|
| 1 | test1 (red 100x100) | white 100x100 (wrong; matches test2's known content) |
| 2 | test2 (white 100x100) | green-top/black-bottom 200x200 (wrong; matches test4) |
| 3 | test3 (blue 100x100) | 4 quadrants RGBY 400x400 (wrong; matches test5) |
| 4 | test4 (green/black 200x200) | red 100x100 (wrong; matches test1) |
| 5 | test5 (quadrants 400x400) | blue 100x100 (wrong; matches test3) |
Pattern: the 5 returned images are a permutation of the 5 sent images. Same set of contents, different order. Parallel Read results appear to be mis-paired with their calls at the Claude Code/model boundary.
Test 2: sequential Read of one file alone
Calling Read on test1_solid_red_100x100.png once, not batched:
- Rendered: solid blue 100x100 (wrong; should be red; matches test3's content).
Calling Read on the same test1_solid_red_100x100.png a third time, still alone, seconds later:
- Rendered: a full-screen browser screenshot at approximately 1920x1080 that had been shared earlier in the session.
The third call returning content from a completely different, previously-seen file indicates the mis-routing is not just intra-batch. It persists across the session's Read history. Prior Read results appear to be cached somewhere and served to later calls with wrong content keys.
Pixel Fingerprint (ground truth from PIL)
For a 1920x1080 screenshot with a white form background:
- Pixel at (0,0): dark (browser chrome background)
- Center pixel: pure white RGB (255, 255, 255), form body
- Edges: dark
For a narrow 1840x189 cropped dark-theme screenshot:
- All sampled pixels: uniformly dark RGB approximately (25, 26, 27)
The model's rendered image interpretations across multiple turns did not match these fingerprints. Independent verification (opening the files in OS image viewer) matched the fingerprints.
Failure Modes (hypotheses)
One or more of:
- Image pipeline caching/routing bug. The Read tool caches rendered images by a key that is not file path plus content hash, and serves stale or misrouted content on repeated calls.
- Call/response pairing bug in parallel Read. Results return in a different order from calls and get mis-paired at the tool-response stitching layer.
- Image dimensions exceed rendering budget. Larger images are downscaled or cropped and lose distinguishing content (but this does not explain the minimal repro with tiny 100x100 images).
- Unknown other.
The minimal repro rules out (3) as sole cause because the test images are well under any plausible rendering budget.
Why this matters
- The model makes confident claims about image content that contradict file pixel data.
- Claims drift between turns for the same file path.
- User-facing trust: multimodal chat becomes unreliable.
- Downstream: if agents act on image content (visual UI testing, OCR-based automation, screenshot-driven workflows), wrong image content produces wrong actions.
Suggested investigation
- Instrument the Read-to-model pipeline for PNG files. Log the path, hash, and bytes or encoded form sent to the model for each Read call.
- Diff across repeated Reads of the same file within one session: does the model receive identical bytes each time, or is a cache serving by wrong key?
- Repro with the 5 synthetic PNGs above (PIL script included below).
- Test across different auth paths (Anthropic, Bedrock, Vertex) for the same symptom.
Reproduction Script
from PIL import Image, ImageDraw
import os
base = '/tmp/image_repro' # adjust for platform
os.makedirs(base, exist_ok=True)
img = Image.new('RGB', (100, 100), (255, 0, 0))
img.save(os.path.join(base, 'test1_solid_red_100x100.png'))
img = Image.new('RGB', (100, 100), (255, 255, 255))
img.save(os.path.join(base, 'test2_solid_white_100x100.png'))
img = Image.new('RGB', (100, 100), (0, 0, 255))
img.save(os.path.join(base, 'test3_solid_blue_100x100.png'))
img = Image.new('RGB', (200, 200), (0, 0, 0))
draw = ImageDraw.Draw(img)
draw.rectangle([0, 0, 200, 100], fill=(0, 255, 0))
img.save(os.path.join(base, 'test4_top_green_bottom_black_200x200.png'))
img = Image.new('RGB', (400, 400), (0, 0, 0))
draw = ImageDraw.Draw(img)
draw.rectangle([0, 0, 200, 200], fill=(255, 0, 0))
draw.rectangle([200, 0, 400, 200], fill=(0, 255, 0))
draw.rectangle([0, 200, 200, 400], fill=(0, 0, 255))
draw.rectangle([200, 200, 400, 400], fill=(255, 255, 0))
img.save(os.path.join(base, 'test5_4quadrants_400x400.png'))
Then, in Claude Code, call Read on each file path once in a batch. Observe returned content does not match known pixel values.
Environment
- OS: Windows 11
- Shell: MSYS2/Git Bash
- Model: Claude Opus 4.7 (1M context) via AWS Bedrock
- Claude Code: 2.1.118 (native win32-x64)
Session transcript and synthetic PNG files available on request. Not posting publicly.
This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗