You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Image-count 400s (Azure: "Exceeded maximum number of images (50)") are not classified as context overflow, so media-strip compaction never triggers #39677
Azure OpenAI hard-caps requests at 50 images. In long agentic sessions where the model reads many images (screenshots, render stills), the full-history resend eventually crosses that cap and every subsequent turn fails with a fatal 400. opencode already ships an auto-recovery designed for exactly this situation — compact, strip media from context, retry — but it never triggers here, because the error is not classified as context_overflow.
Environment
opencode 1.18.5
model openai/gpt-5.6-terra via OpenRouter, routed to the Azure upstream
What happens
A session accumulated 47 image read tool results; the next user turn attached 4 more images and failed with:
{"error":{"message":"Provider returned error","code":400,"metadata":{"raw":"{\n\"error\": {\n\"message\": \"Exceeded maximum number of images (50) allowed in the request.\",\n\"type\": \"invalid_request_error\",\n\"param\": \"input\",\n\"code\": null\n }\n}","provider_name":"Azure","is_byok":false}}}
The turn surfaces as a plain non-retryable api_error. Because the oversized history is resent on every turn, the session is permanently poisoned from this point — no retry can succeed, only compaction/media-stripping could.
Root cause
parseAPICallError (packages/opencode/src/provider/error.ts) classifies an error as context_overflow only when one of these holds:
isContextOverflow(message) matches (packages/llm/src/provider-error.ts) — the pattern list is entirely token/length phrases (prompt is too long, context_length_exceeded, too many tokens, …),
HTTP status is 413,
body.error.code === "context_length_exceeded".
Azure's image-count error matches none of them: it is a 400 (not 413), the inner code is null (and OpenRouter's wrapper reports numeric code: 400), and no pattern mentions images.
When classification does return context_overflow, SessionCompaction runs with overflow: true, which compacts, strips media from context, and auto-continues with "The previous request exceeded the provider's size limit due to large media attachments…" (packages/opencode/src/session/compaction.ts) — exactly the right remedy for this failure.
Suggested fix
Add an image-count pattern to patterns in packages/llm/src/provider-error.ts:
/maximumnumberofimages/i,
message() appends the raw response body, so the pattern matches even through OpenRouter's double-wrapped payload (the Azure message is nested as an escaped string inside metadata.raw). Other providers have similar per-request media-count caps; the Azure 50-image message is the one observed in the wild.
Repro sketch
Run a session on an Azure-served OpenAI model (directly, or via OpenRouter with provider.order: ["azure"]).
Have the agent read 50+ images across turns (or attach them as file parts).
The next turn fails with the fatal 400 above instead of compacting with media-strip and continuing.
Hit the same root cause via a second Azure cap — the per-request payload byte limit rather than the image count:
Request content length exceeded 32 MB limit.
Same classification gap you identified: it's a 400 (not 413), inner code is null, and no pattern describes a byte-size cap. Via OpenRouter it arrives double-wrapped as code: 400 with the Azure message escaped inside metadata.raw, exactly as in your report.
Worth noting #14562 already fixed this semantic failure for the 413 spelling (/request entity too large/i + the statusCode === 413 check). Azure expresses the same "payload too big" condition as a 400 with prose, so it bypasses the fix built for it. The near-miss is /context[_ ]length[_ ]exceeded/i — Azure says "content length exceeded", not "context length exceeded".
Confirmed both patterns are still absent from the shipped 1.18.5 binary:
ABSENT : content length exceeded
ABSENT : maximum number of images
PRESENT : request entity too large
PRESENT : prompt is too long
#40167 adds /content length exceeded/i alongside your suggested /maximum number of images/i, with tests for the bare, status-prefixed, and OpenRouter-wrapped forms of both. Verified they don't regress the throttling exclusions from #39623.
For anyone stuck right now: the session is unrecoverable in-product, but you can strip the base64 from old read parts directly in opencode.db (state.attachments[].url; state.output is only "Image read successfully", so no conversation history is lost). That took mine from 76.7 MB to 8.08 MB and it resumed normally. Mine held 48 images — two shy of also tripping the 50-image cap in this issue.
Happened to me too with another model, same lock, tried with "continue" but it keeps resending full history so it never recovers:
My error was:
[invalid_request_error] request contains 51 images, exceeding the maximum of 50 allowed per request
My temporary fix was to strip oldest image attachments directly in opencode.db: 51 -> 15 (state.attachments=[], keep filePath in text so agent can re-read), then full restart. So the session was unlocked without context loss.
Note for #40167: /maximum number of images/i doesn't match this wording.
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.
Activity
dotCipher commentedon Aug 2, 2026
Hit the same root cause via a second Azure cap — the per-request payload byte limit rather than the image count:
Same classification gap you identified: it's a 400 (not 413), inner
codeisnull, and no pattern describes a byte-size cap. Via OpenRouter it arrives double-wrapped ascode: 400with the Azure message escaped insidemetadata.raw, exactly as in your report.Worth noting #14562 already fixed this semantic failure for the 413 spelling (
/request entity too large/i+ thestatusCode === 413check). Azure expresses the same "payload too big" condition as a 400 with prose, so it bypasses the fix built for it. The near-miss is/context[_ ]length[_ ]exceeded/i— Azure says "content length exceeded", not "context length exceeded".Confirmed both patterns are still absent from the shipped 1.18.5 binary:
#40167 adds
/content length exceeded/ialongside your suggested/maximum number of images/i, with tests for the bare, status-prefixed, and OpenRouter-wrapped forms of both. Verified they don't regress the throttling exclusions from #39623.For anyone stuck right now: the session is unrecoverable in-product, but you can strip the base64 from old
readparts directly inopencode.db(state.attachments[].url;state.outputis only"Image read successfully", so no conversation history is lost). That took mine from 76.7 MB to 8.08 MB and it resumed normally. Mine held 48 images — two shy of also tripping the 50-image cap in this issue.typesafeui commentedon Sep 9, 2026
Happened to me too with another model, same lock, tried with "continue" but it keeps resending full history so it never recovers:
My error was:
My temporary fix was to strip oldest image attachments directly in opencode.db: 51 -> 15 (state.attachments=[], keep filePath in text so agent can re-read), then full restart. So the session was unlocked without context loss.
Note for #40167: /maximum number of images/i doesn't match this wording.
sawa-ko commentedon Sep 19, 2026
I'm having the same problem (with deepseek) here; it's a pain because the session gets lost since it gets stuck in that infinite error loop.
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 𝕺𝖕𝖊𝖓𝕮𝖔𝖉𝖊