Skip to content

Attached images degrade to a bare text placeholder when the model lacks vision — agent has no way to read them #42758

Closed as not planned

Description

@sumwcc

Summary

When a user pastes images into a chat and the active model does not support vision (e.g. a text-only model like deepseek-v4-flash), the image file parts are replaced by a plain text placeholder before being sent to the model. The agent then has no way to access the actual image content — the placeholder carries no reference to how the image can be retrieved.

Current behavior

In packages/opencode/src/session/message-v2.ts (around line 213):

if (options?.stripMedia && isMedia(part.mime)) {
  userMessage.parts.push({
    type: "text",
    text: `[Attached ${part.mime}: ${part.filename ?? "file"}]`,
  })
}

So the model sees only [Attached image/png: image-2.png]. There is no session/message/part id, no API reference, no data URL — nothing the agent could use to fetch the image itself. The only way to recover the image is to query the local opencode.db (the part table stores {type:"file", mime, filename, url} with session_id/message_id) or hit the local serve API, which is undocumented for this purpose and requires guessing the current session.

Why it matters

This forces workarounds: to read a pasted image, an agent (or a skill like an "image recognition" skill) has to:

  1. guess the current session (no env var exposes session_id),
  2. query the sqlite DB / serve API to find the most recent image part,
  3. forward the data: URL to a separate vision model.

That's fragile (parallel sessions race) and entirely non-obvious from the placeholder alone.

Proposal (any of these would help)

  1. Include a retrievable reference in the placeholder, e.g. [Attached image/png: image-2.png] (session: …, message: …, part: …) or a URL like opencode://session/{sessionID}/message/{messageID}/part/{partID} the agent can dereference.
  2. Expose the current session id to agents (e.g. OPENCODE_SESSION_ID env var when running tool calls) so tools/skills can scope queries precisely.
  3. A config option to keep sending lightweight media metadata (mime, filename, session/message/part ids) to text-only models instead of dropping it.

Environment

  • opencode version: 1.18.18 (OpenChamber distribution, opencode serve)
  • Model: deepseek-v4-flash (no vision)
  • OS: macOS

Happy to help with a PR if a direction is agreed.

Activity

  1. github-actions commented on Aug 15, 2026

    @github-actions
    Contributor

    This issue might be a duplicate of existing issues. Please check:

    If your issue adds meaningful new detail or a distinct angle not covered by those, feel free to clarify the difference so maintainers can decide whether to merge them.

  2. sumwcc commented on Aug 16, 2026

    @sumwcc
    Author

    Thanks for the duplicate check — #36006 is indeed the same root problem and its proposals are excellent. I want to clarify one gap my report adds that #36006 does not cover: images pasted from the clipboard have no local file path at all.

    The paste scenario

    When a user pastes an image (rather than @-referencing a file), the image never touches disk:

    • It is stored as a data:image/...;base64,... URL inside the part table of opencode.db (verified: {type:"file", mime:"image/png", filename:"image-2.png", url:"data:..."} with session_id/message_id).
    • filename is a synthetic name (image-2.png), not a path.

    So for pasted images, none of #36006's options help directly:

    • Option C (put the file path in the replacement text) — there is no path to put there.
    • Options A/B (plugin hook / custom template pointing at a path) — same problem.

    The only way to recover a pasted image is via the session/message/part identity (querying the local DB or the serve API GET /session/:id/message). The agent currently cannot know even the current session_id (no env var, no context injection).

    Concrete suggestion to cover both scenarios

    If #36006's Option A hook were extended to include the original part's identity (or its data: URL) rather than only a file path, both scenarios would be covered:

    input: {
      sessionID: string
      messageID?: string
      partID?: string
      dataURL?: string       // for pasted media that never touched disk
      filename?: string
      mime: string
      ...
    }

    That way a plugin can guide the LLM to a tool that reads pasted images from the DB/serve API (which is exactly the workaround my team built: a skill that pulls the latest image part via the serve API and forwards it to a separate vision model).

    Also worth noting: exposing the current session_id to agents (e.g. an OPENCODE_SESSION_ID env var for tool calls) would fix the related issue #33035 and remove the need to guess the session at all.

    Happy to consolidate this detail into #36006 if maintainers prefer a single issue.

  3. thdxr commented on Oct 11, 2026

    @thdxr
    Member

    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 𝕺𝖕𝖊𝖓𝕮𝖔𝖉𝖊

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

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions