Repository navigation
[BUG] Agent accumulated 51 images via read tool, session bricked by provider 50-image count limit with no recovery #47487
Description
Description
Agent bricked its own session by accumulating images, user got stuck and cannot chat anymore.
Session ses_f9337a042ffebyl9CiHpjtKvlu titled Facetworks (816 messages, 3377 parts) ended in an unrecoverable loop:
Error from provider (Console Go): Upstream request failed: [invalid_request_error] request contains 51 images, exceeding the maximum of 50 allowed per request
Full error from DB:
- statusCode 400, isRetryable False
- responseBody:
{"model":"muse-spark-1.3-contributor","error":{"param":null,"type":"invalid_request_error","message":"Error from provider (Console Go): Upstream request failed: [invalid_request_error] request contains 51 images, exceeding the maximum of 50 allowed per request"}} - metadata url:
https://opencode.ai/zen/go/v1/responses - first failure 2026-09-05T13:13:44Z, then every retry fails identically including plain user messages.
What exactly happened, verified from local ~/.local/share/opencode/opencode.db:
- 51 parts with image attachments in this session
- 48x
readtool reading screenshots:/tmp/profile-readme/covers/*.png,/tmp/undertow-shot*.png,/tmp/lichen-*.png,/tmp/abyss-shot*.png,/tmp/drift-shot*.png,/tmp/facet-wide.png,/tmp/facet-mobile.png,/tmp/gummy-*.png,/tmp/jelly-thumb.jpg,/tmp/src-check.png, plusprojects/lichen/docs/screenshot.pngandprojects/drift/docs/screenshot.png - 3x
huggingface_gr1_z_image_turbo_generateoutputs withmime image/webpattachments - Agent was doing visual verification loops across undertow, lichen, abyss, drift, facetworks, gummy. User did not paste 51 images manually.
Why it is bricked:
- History already contains 51 images, so every subsequent LLM call fails before the model runs.
- User tried
okay continue now?,use less images then,can something be done?- all failed with the same 51-image error. - No way to
/compact, fork without images, prune old tool attachments, or delete messages from TUI to recover.
Expected:
- OpenCode should enforce a client-side image budget before hitting the provider (warn, stop attaching old images, downscale to text placeholder, or auto-prune oldest image attachments).
- Compaction and retry paths should strip or summarize images so a session never becomes permanently dead.
- Provide recovery: remove message, fork from message without images, or
compactthat excludes image bytes.
Related but not duplicates (all size or invalid file, not count):
- Request Entity Too Large with images blocks session — compaction also fails #14562 Request Entity Too Large with images blocks session, compaction also fails
- Session becomes permanently dead when image exceeds provider size limit — no recovery possible #21668 Session dead when image exceeds 5 MB limit
- Trying to read an invalid image file bricks the session #9512 Trying to read an invalid image file bricks the session
- request_too_large: Request exceeds the maximum size // exceded pixels image #13865 request_too_large pixels image
Plugins
No response - stock build agent with huggingface MCP image tool available
OpenCode version
1.18.27 at session time, binary now 1.18.29
Steps to reproduce
- Start session on
opencode-go/muse-spark-1.3-contributorvariant xhigh in a repo with browser screenshot workflow - Let agent iteratively take screenshots to /tmp and
readthem for visual check (repeat 40+ times) plus a few image generations - Once attachment count in history exceeds 50, next step fails with 400 invalid_request_error
- Send any follow-up message, it fails with the same error. Session cannot continue.
Screenshot and/or share link
No share link, private code. Error text above is verbatim from session message msg_071b40311001TNVEKL93snfrs2. Can provide DB part counts on request.
Operating System
Ubuntu 24.04.4 LTS, kernel 7.0.0-30-generic
Terminal
xterm-256color via OpenCode TUI/CLI

Activity
github-actions commentedon Sep 5, 2026
This issue might be a duplicate of an existing issue. Please check:
context_overflowso compaction/media-strip never triggers. That issue also includes a suggested fix and covers the Azure and OpenRouter variants of the same provider cap.royalpinto007 commentedon Sep 5, 2026
Fix proposed in #47493: classify image-count rejections as context overflow and cap outgoing images at 40 newest per request with placeholders for older ones. Verified with new tests plus existing session suites.
Sugamsss commentedon Sep 9, 2026
We hit this exact problem while doing visual verification loops with Playwright — once you accumulate screenshots, sessions hit 413 payload caps or provider count limits (like the 50-image ceiling here). When that happens, the session gets wedged because the raw base64 stays in the transcript.
While core handling is being designed, I put together a zero-dependency plugin that solved this for our workflow: opencode-prune-images (added in PR #48096).
How it works:
contextright before dispatch so your persisted SQLite history is never touched or corrupted.readtool to pull that specific image back into the active 7 slots./tmpscreenshots and pasted base64 data to a small rolling local cache (~/.cache/opencode/recent-images/, capped at 100 files FIFO) so images don't vanish if/tmpgets cleared.Sharing here in case it helps unblock anyone running into this right now.
ez2sarang commentedon Sep 16, 2026
Same failure on 1.18.30, provider
opencode/muse-spark-1.3-contributor-free(Console), macOS 26.6.2: a UI-iteration session where the agentread52 screenshots ended withand every later prompt (plain text included) failed identically. Auto-recovery never ran, consistent with the classification gap in #39677.
A second gap that I don't think has been mentioned yet: the existing prune pass can't help even when it does run, because it budgets by text only:
SessionCompaction.prunewalks old completed tool parts and sumsToken.estimate(part.state.output)againstPRUNE_PROTECT = 40_000(https://github.com/anomalyco/opencode/blob/3104c1428e/packages/opencode/src/session/compaction.ts#L286-L318, estimate at L299).read,state.outputis the 23-char stringImage read successfully; the attachment (base64 bytes, image count) is not part of the estimate. So 52 screenshots ≈ 0 tokens, the budget is never exceeded and nothing getstime.compacted.Suggestion, complementary to #47493's per-request cap: make prune count attachments too (a fixed token weight per image attachment, and/or an attachment-count budget), so old screenshots get
time.compactedlike any other large tool output. Once the flag is set,toModelMessagesalready does the right thing:[Old tool result content cleared]+ attachments dropped (https://github.com/anomalyco/opencode/blob/3104c1428e/packages/opencode/src/session/message-v2.ts#L293-L296).Recovery that works today for a bricked session (same mechanism prune uses, fully reversible — just delete the key):
Back up
opencode.dbfirst. Verified on 1.18.30: 35 old image parts flagged → 17 images sent → session usable again, no restart needed (history is re-read from the DB on each turn). Onlystate.time.compactedis touched — don't put anything non-object into the part's top-levelmetadata, that field is forwarded asproviderOptionsand will fail the AI SDKModelMessage[]schema.Also, reading the code,
/compacton 1.18.30 serializes history to text with[Attached image/png: file]placeholders (compaction.ts L73-L78), so it shouldn't hit the image cap either; I didn't test it because I'd already recovered via the flag.Sugamsss commentedon Sep 17, 2026
Thanks for the clear notes. We hit the same wall. Past the image cap, even plain text fails and recovery never kicks in.
Your second gap looks right. Prune budgets text output only, so image reads barely count and nothing gets flagged. Counting attachments makes sense, next to the cap in 47493. And once flagged, core already drops attachments as it should.
Thanks for the reversible fix too. Backup first, touch only time.compacted, stay out of metadata. Noted on providerOptions.
On compact, you are right. V1 uses text placeholders, so it should not hit the cap like normal dispatch. Forcing compaction on every image cap still feels heavy to me. It rewrites history to clear old results. I prefer a rolling window first. History stays intact, newest images stay raw, older ones become cards.
That is what my plugin does. Latest 7 within 16 MiB stay raw. Older ones become small cards with path and context. No DB writes. I also fixed pruned V1 attachments breaking downstream shapes. They now leave the array and their cards join the tool output.
Plugin here if useful: https://github.com/Sugamsss/opencode-prune-images
Shared goal. Sessions should not die from screenshots.
thdxr commentedon Oct 11, 2026
Thanks for the report! OpenCode 2.0 is now out and development is focused on V2, so we're closing issues filed against OpenCode 1. Please upgrade (
opencode upgrade --major, details: https://anoma.ly/notes/opencode-2-0), and if this is still relevant on 2.0, reopen this issue or open a new one with your 2.0 version.— from 𝕺𝖕𝖊𝖓𝕮𝖔𝖉𝖊