Skip to content

session: pasted/dropped images are persisted but silently stripped from the model request on v2 beta #45986

Closed as not planned

Description

@tsugumi42

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.ts fail to load with SchemaError: 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

  1. Start opencode2, select built-in provider merge-gateway, model zai/glm-5.3-flash. The resolved entry uses package aisdk:merge-gateway-ai-sdk-provider with capabilities.input including image.
  2. In the TUI, press Ctrl+V to paste an image into the prompt (it shows as [Image 1]), send "describe this image".
  3. The model replies it cannot see any image — it only received the literal text [Image 1].
  4. GET /api/session/{id}/message/{msgID} confirms the message persisted files[0]: { mime: "image/png", source: { type: "inline" }, data: <~1 MB base64> }; the server log shows no error at send time.
  5. 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

  1. LoikStyle commented on Aug 31, 2026

    @LoikStyle

    Cross-reference: related but not a duplicate of #46419 / PR #46418.

    #46418 fixes client-side image attachment failing on insecure origins (crypto.subtle in draft-store.ts). This issue is a different layer — server-side stripMedia / message-v2 dropping persisted image parts from the model request. Not addressed by my PR.

  2. rssitnt commented on Oct 16, 2026

    @rssitnt

    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 serve behind a custom provider. Adding our diagnosis since it narrows the fix.

    Setup

    • opencode serve on localhost, spawned by an agent daemon (Paseo)
    • Custom provider: @ai-sdk/anthropic adapter pointing to a local proxy that forwards to a vision-capable model pool
    • Model config in opencode.json declares capabilities.input.image: true and input_modalities: ["text", "image"]
    • The provider's /v1/models endpoint returns input_modalities: ["text", "image"] and vision: true

    Behavior

    Images attached to user messages are stored as type: "file" parts with mime: "image/png" and a data: 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:

    ERROR: Cannot read "attachment-1.png" (this model does not support image input). Inform the user.
    

    Root cause (from binary analysis of v1.19.0)

    The request builder contains a gate at approximately offset ~98M in the compiled binary:

    G = wy(part.mime);           // maps MIME → modality string ("image", "audio", "pdf", "text")
    if (!G) return part;         // unknown → pass through
    if (Z.capabilities.input[G]) return part;  // model supports → pass through
    // else:
    return { type: "text", text: `ERROR: Cannot read ...` };  // ← STRIPPED HERE

    The problem: Z.capabilities does not reflect the provider's /v1/models response or the config-file capabilities. We verified both sources declare image: true, but the gate still rejects. The source of Z appears to be an internal model catalog that doesn't resolve custom/self-hosted providers correctly in the serve path.

    What we tried (all ineffective)

    1. Full capabilities block in opencode.json model entry (all fields: temperature, reasoning, attachment, toolcall, input/output with all modalities, interleaved) — gate still fires
    2. input_modalities in model config — gate still fires
    3. Provider's /v1/models returning input_modalities: ["text", "image"] — gate still fires
    4. Switching from @ai-sdk/openai-compatible to @ai-sdk/anthropic — same result (file parts stripped before adapter is invoked)
    5. Dev build 0.0.0-dev-202610141901 (26 image_url references vs 10 in stable) — still converts file parts to text in serve path
    6. Binary patching the gate — not possible; Bun validates embedded JS at startup

    What works (proving the model and provider are fine)

    Direct Anthropic-format API calls to the same provider with proper image content 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.capabilities from the provider's /v1/models response (or the config-file capabilities block) rather than from what appears to be a hardcoded catalog. Alternatively, file parts with image/* MIME should be converted to image_url (OpenAI) or source.base64 (Anthropic) content blocks before the capability check, since the adapters already know how to handle them.

    This affects any deployment using opencode serve with custom providers — which is a growing use case for agent frameworks that embed OpenCode as a backend.

  3. opencodejr commented on Oct 28, 2026

    @opencodejr

    Thanks for the detailed report. I couldn't reproduce this on the latest release (2.2.0) or on the current dev branch (506ac20). The exact beta you reported is no longer published, so I couldn't test that build.

    What I tried:

    • A simulated vision model (capabilities.input includes image), using both the built-in OpenAI-chat provider and an aisdk:@ai-sdk/openai-compatible provider package.
    • Pasted/dropped a PNG into the prompt: it showed as [Image 1] with a file chip, then I sent "describe this image".
    • Each time, the request sent to the model had both the text and the image (image_url with a data: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 the merge-gateway provider package (merge-gateway-ai-sdk-provider), which I can't test without an account, or something since fixed after your beta.

    These would help:

    • Does it still happen after upgrading to the latest release (@opencode/cli@2.2.0)?
    • If it does, does it also happen with a different vision model or provider (for example a first-party Anthropic or OpenAI model)? That would show whether it's specific to merge-gateway.
    • Any server log lines from around the time you send the message, if available.

    Related: #41722 (attached AVIF images silently dropped on v2), #42758 (placeholder on models without vision), #20802 (custom OpenAI-compatible providers and image attachments).

  4. opencodejr commented on Oct 31, 2026

    @opencodejr

    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 an aisdk: provider package. Your exact beta is no longer published, and the merge-gateway provider 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 Read it. Thanks again for the detailed report!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions