Repository navigation
Description
Description
On v2 beta, images attached to a user message (Ctrl+V paste or drag-and-drop) are persisted as image/* file parts in the session store, but the model request contains only the literal text [Image 1]. The selected model declares image input in its resolved capabilities, and nothing is logged or shown — the image is silently stripped. Tool-result images (e.g. Read on a local PNG) do reach the model in the same session, which isolates the failure to the user-message → model-request path.
Suspect: the stripMedia placeholder path in packages/opencode/src/session/message-v2.ts (see #42758) firing even though the model declares image input.
Plugins
clock-hook.js,identity.js(loaded)luvus.js,rtk.tsfail to load withSchemaError: Missing key at ["default"](pre-existing, unrelated — the issue reproduces regardless)
OpenCode version
opencode2 v0.0.0-beta-18414 (npm @opencode-ai/cli, beta channel)
Steps to reproduce
- Start
opencode2, select built-in providermerge-gateway, modelzai/glm-5.3-flash. The resolved entry uses packageaisdk:merge-gateway-ai-sdk-providerwithcapabilities.inputincludingimage. - In the TUI, press Ctrl+V to paste an image into the prompt (it shows as
[Image 1]), send "describe this image". - The model replies it cannot see any image — it only received the literal text
[Image 1]. GET /api/session/{id}/message/{msgID}confirms the message persistedfiles[0]: { mime: "image/png", source: { type: "inline" }, data: <~1 MB base64> }; the server log shows no error at send time.- Repeat with a drag-dropped JPEG — same result (persisted, model sees only the placeholder).
Control: same session and model, Read tool on a local PNG → the image reaches the model and is described correctly.
Screenshot and/or share link
Not shared (session contains unrelated personal content). Evidence excerpts:
// GET /api/model (excerpt)
{ "providerID": "merge-gateway", "id": "zai/glm-5.3-flash",
"package": "aisdk:merge-gateway-ai-sdk-provider",
"capabilities": { "tools": true, "input": ["text","image","video","pdf"], "output": ["text"] } }// GET /api/session/{id}/message/{msgID} (excerpt, base64 truncated)
{ "type": "user", "text": "[Image 1] <prompt>",
"files": [ { "data": "iVBORw0KGgo...(len 1035512)", "mime": "image/png",
"source": { "type": "inline" }, "name": "1787906587870_d.png" } ] }Operating System
Windows 10 (10.0.19045), x64
Terminal
Windows Terminal (pwsh 7), opencode2 TUI
Related: #39083 (v1, intermittent — this is consistent on v2 beta), #42758 (covers models without vision; here the model declares vision and is still stripped), #20802 / #33542 (custom OpenAI-compatible providers surface an explicit error instead of silently stripping), #44310 (paste doesn't attach at all; here it attaches and persists, failing downstream).
Workaround: save the image to a file and have the agent Read it — tool-result images reach the model.
Activity
LoikStyle commentedon Aug 31, 2026
Cross-reference: related but not a duplicate of #46419 / PR #46418.
#46418 fixes client-side image attachment failing on insecure origins (
crypto.subtleindraft-store.ts). This issue is a different layer — server-sidestripMedia/ message-v2 dropping persisted image parts from the model request. Not addressed by my PR.rssitnt commentedon Oct 16, 2026
Confirming on v1.19.0 serve path with a custom provider — root cause analysis
We hit this exact behavior on v1.19.0 (not just v2 beta) when running
opencode servebehind a custom provider. Adding our diagnosis since it narrows the fix.Setup
opencode serveon localhost, spawned by an agent daemon (Paseo)@ai-sdk/anthropicadapter pointing to a local proxy that forwards to a vision-capable model poolopencode.jsondeclarescapabilities.input.image: trueandinput_modalities: ["text", "image"]/v1/modelsendpoint returnsinput_modalities: ["text", "image"]andvision: trueBehavior
Images attached to user messages are stored as
type: "file"parts withmime: "image/png"and adata:URL in the session. The timeline correctly shows[Image]. However, the outgoing model request contains only text parts — the file part is silently replaced with:Root cause (from binary analysis of v1.19.0)
The request builder contains a gate at approximately offset ~98M in the compiled binary:
The problem:
Z.capabilitiesdoes not reflect the provider's/v1/modelsresponse or the config-file capabilities. We verified both sources declareimage: true, but the gate still rejects. The source ofZappears to be an internal model catalog that doesn't resolve custom/self-hosted providers correctly in the serve path.What we tried (all ineffective)
capabilitiesblock inopencode.jsonmodel entry (all fields: temperature, reasoning, attachment, toolcall, input/output with all modalities, interleaved) — gate still firesinput_modalitiesin model config — gate still fires/v1/modelsreturninginput_modalities: ["text", "image"]— gate still fires@ai-sdk/openai-compatibleto@ai-sdk/anthropic— same result (file parts stripped before adapter is invoked)0.0.0-dev-202610141901(26image_urlreferences vs 10 in stable) — still converts file parts to text in serve pathWhat works (proving the model and provider are fine)
Direct Anthropic-format API calls to the same provider with proper
imagecontent blocks work correctly — the model sees and describes the image. The issue is exclusively in the serve-path request builder.Suggested fix direction
The gate should resolve
Z.capabilitiesfrom the provider's/v1/modelsresponse (or the config-filecapabilitiesblock) rather than from what appears to be a hardcoded catalog. Alternatively, file parts withimage/*MIME should be converted toimage_url(OpenAI) orsource.base64(Anthropic) content blocks before the capability check, since the adapters already know how to handle them.This affects any deployment using
opencode servewith custom providers — which is a growing use case for agent frameworks that embed OpenCode as a backend.opencodejr commentedon Oct 28, 2026
Thanks for the detailed report. I couldn't reproduce this on the latest release (2.2.0) or on the current
devbranch (506ac20). The exact beta you reported is no longer published, so I couldn't test that build.What I tried:
capabilities.inputincludesimage), using both the built-in OpenAI-chat provider and anaisdk:@ai-sdk/openai-compatibleprovider package.[Image 1]with a file chip, then I sent "describe this image".image_urlwith adata:image/png;base64,...URL), not just the[Image 1]text.On current code, an image is only replaced with text when the model doesn't declare image input, and then it becomes a visible
ERROR: Cannot read…note rather than a bare[Image 1]. So what you saw is most likely either specific to themerge-gatewayprovider package (merge-gateway-ai-sdk-provider), which I can't test without an account, or something since fixed after your beta.These would help:
@opencode/cli@2.2.0)?merge-gateway.Related: #41722 (attached AVIF images silently dropped on v2), #42758 (placeholder on models without vision), #20802 (custom OpenAI-compatible providers and image attachments).
opencodejr commentedon Oct 31, 2026
Closing this for now, since we couldn't reproduce it and haven't heard back. On 2.2.0 and on
dev(506ac20), a pasted or dropped image reached the model request as an image part, with both the built-in OpenAI-chat provider and anaisdk:provider package. Your exact beta is no longer published, and themerge-gatewayprovider package can't be tested without an account.If it still happens on a current release, please reply here or open a new issue. It would help to know whether it also happens with a different vision-capable provider or model, and to include any server log lines from when you send the message. Workaround in the meantime: save the image to a file and have the agent
Readit. Thanks again for the detailed report!