anomalyco / anomalyco/opencode
read tool trusts the file extension when magic bytes don't match: a corrupt .png is attached as image/png and bricks the session (400 invalid image data on every request)
@kitlangton is already working on this.
Since Sep 11, 2026.
- Dominant language
- TypeScript
- Stars
- 209k
- Forks
- 27.5k
- PR merge metrics
- PR metrics pending
Description
Description
If the agent calls read on a .png file whose first bytes are not a PNG signature, the tool still reports Image read successfully and attaches the whole file as data:image/png;base64,.... The attachment is stored in the session and replayed on every following request, so the provider rejects every message from then on:
Error from provider (Console): Upstream request failed: [invalid_request_error] invalid image data at input[34].output[1]: the `image/png` payload could not be decoded (it may be corrupt or truncated).
There is no way to recover from inside OpenCode: new prompts, /compact attempts and plugin continuations all fail with the same error, because the tool result with the bad attachment is always included.
Root cause (unchanged on dev @ 193de13a88):
sniffAttachmentMime()returns the extension-based fallback when no magic bytes match: https://github.com/anomalyco/opencode/blob/193de13a88/packages/opencode/src/util/media.ts#L15-L26readthen treats that mime as an image and attaches the raw bytes without checking they are decodable: https://github.com/anomalyco/opencode/blob/193de13a88/packages/opencode/src/tool/read.ts#L303-L325
How it happened for real: adb exec-out screencap -p > shot.png on a multi-display Android phone writes a text warning ([Warning] Multiple displays were found, but no display id was specified! ..., 347 bytes) to stdout before the PNG bytes. file shot.png says data, but read happily attached it as image/png.
Expected: when the sniffed bytes don't match an image/PDF signature, don't attach the file as media (return an error like "file is not a valid PNG", or fall back to the text/binary path). Ideally also a recovery path: when a provider rejects a tool-result media part (400 invalid image), strip that attachment from history instead of failing forever. Related: #47487 (session bricked by image count, no recovery), #41163 (tool-result media 400s whole request).
Workaround that worked: clear state.attachments of that tool part in ~/.local/share/opencode/opencode.db (part table); the same session then works again.
Plugins
oh-my-opencode 4.19.4
OpenCode version
1.14.17 (code path verified identical on dev 193de13a88, 2026-09-10)
Steps to reproduce
- Make a "png" that is text followed by a real PNG:
printf '[Warning] Multiple displays were found, but no display id was specified!\n' > bad.png cat some-valid.png >> bad.png file bad.png # -> "data" (not a PNG) - In OpenCode (any provider that accepts images; here provider
opencode, modelmuse-spark-1.3-contributor-free), ask the agent toread bad.png. Tool output:Image read successfully, with animage/pngattachment. - Send any next message. Provider returns 400
invalid image data ... could not be decoded. - Every further message in that session fails identically (the attachment is replayed from the session DB each time).
Screenshot and/or share link
No share link (session contains personal screenshots). The provider error text above is copied verbatim from the log (service=llm ... error=AI_APICallError ... statusCode 400).
Operating System
macOS 26.6.2 (Darwin 25.6.0, arm64)
Terminal
ghostty
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.