Skip to content

AI SDK route (aisdk:@ai-sdk/openai-compatible) sends tool-result images as null content parts #50960

Closed

Description

@krishna2206

Description

With a provider on the AI SDK route aisdk:@ai-sdk/openai-compatible, an image returned by a tool (e.g. read on a PNG) is sent to the provider as a null content part instead of an image_url part.

opencode moves tool-result media into a follow-up user message (packages/core/src/aisdk.ts, "Attached media from tool result:"), but after the AI SDK conversion the image part is null:

{
  "role": "user",
  "content": [
    { "type": "text", "text": "Attached media from tool result:" },
    null
  ]
}

Consequences:

  • the model never sees the image;
  • any server that iterates the content parts without a null check fails the request, and since the history is replayed on every turn, the session stays broken from then on (we hit this with a local OpenAI-compatible proxy: null is not an object (evaluating 'part.type')).

The native route does not have the problem: with package: "@opencode/ai/providers/openai-compatible" the same turn sends
{ "type": "image_url", "image_url": { "url": "data:image/png;base64,..." } }, and the model reads the image correctly.

Side difference, same capture: on the AI SDK route the preceding tool message content is the JSON-encoded array ('[{"type":"text","text":"Image read successfully"}]'), while the native route sends the plain text.

Steps to reproduce

  1. Declare a provider with package: "aisdk:@ai-sdk/openai-compatible" and a model whose capabilities.input includes image, pointing baseURL at a server that logs request bodies.
  2. Let the model call read on a PNG file.
  3. Inspect the next /v1/chat/completions request: the last user message is ["text", null].

Switching only the provider package to @opencode/ai/providers/openai-compatible makes the same request carry the image_url part.

OpenCode version

2.0.15

Operating system

macOS 15 (Darwin 24.6.0)

Activity

  1. JosvanderWesthuizen commented on Sep 26, 2026

    @JosvanderWesthuizen

    I was able to reproduce this on v2.0.15 (built from the tag), but the config in the issue as written doesn't trigger it on its own.

    The literal config doesn't reach the AI SDK path. aisdk:@ai-sdk/openai-compatible matches the rewrite table in AISDKNative.rewrite() (packages/core/src/aisdk-native.ts), so it gets routed to @opencode/ai/providers/openai-compatible. The request comes out identical to the native route (correct image_url, no "Attached media from tool result:" text).

    A version-pinned specifier does reproduce it. Both of these skip the rewrite, install @ai-sdk/openai-compatible@3.0.57 at runtime, and send the null part:

    "package": "aisdk:@ai-sdk/openai-compatible@3"
    // or
    "package": "aisdk:@ai-sdk/openai-compatible@latest"
    {"role":"tool","tool_call_id":"call_read_png","content":"[{\"type\":\"text\",\"text\":\"Image read successfully\"}]"}
    {"role":"user","content":[{"type":"text","text":"Attached media from tool result:"},null]}

    @krishna2206, did your config have a version suffix on the package, or something similar that avoided the rewrite?

    Cause. @ai-sdk/openai-compatible 3.x implements specificationVersion: "v4", where a file part's data is a tagged object ({ type: "data" | "url" | ... }). fileData() in packages/core/src/aisdk.ts still returns the v3 shape (a raw base64 string / bytes / URL). The converter's switch (part.data.type) matches no case and returns undefined, which serializes to null. The same route with @ai-sdk/openai-compatible@2.0.41 (the version the monorepo pins) sends a correct image_url part.

    The JSON-encoded tool message content from the original report happens on both 2.0.41 and 3.0.57, so that one looks like a separate issue.

    package tool-result image
    aisdk:@ai-sdk/openai-compatible rewritten to native, image_url ✅
    aisdk:@ai-sdk/openai-compatible@3 / @latest (3.0.57) null ❌
    AI SDK route with 2.0.41 image_url ✅
    @opencode/ai/providers/openai-compatible image_url ✅

    Suggested fixes:

    • Detect specificationVersion on AI SDK models and emit the v4 part shape (or reject v4 models with a clear error instead of silently sending null).
    • Pin runtime installs of aisdk: packages to a compatible major when no version is given.

    Repro (mock OpenAI-compatible server that logs request bodies and scripts a read call on a PNG, all six provider variants above): https://gist.github.com/JosvanderWesthuizen/b300022e40b6da969889ea579a2a03cd

  2. Ethereal49 commented on Sep 26, 2026

    @Ethereal49

    I’m working on a v2 fix for versioned AI SDK providers using the v4 file-data contract, with regression coverage for v3 and v4 media serialization.

  3. rekram1-node commented on Sep 26, 2026

    @rekram1-node
    Collaborator

    On v2 you shouldn't be using the aisdk. We will eventually drop support for it altogether, why are u using the aisdk instead of the native route?

    @opencode/ai/providers/openai-compatible

  4. Ethereal49 commented on Sep 27, 2026

    @Ethereal49

    @rekram1-node Thanks for clarifying the v2 direction. I'm contributing a fix for this report rather than relying on the AI SDK route for a separate use case.

    PR #51482 targets the versioned specifiers described above (aisdk:@ai-sdk/openai-compatible@3 / @latest): they bypass the native rewrite and can silently serialize image parts as null because of the v3/v4 file-data contract mismatch. I agree that @opencode/ai/providers/openai-compatible is the preferred route here.

    Given the plan to remove AI SDK support, would you prefer a clear error for unsupported AI SDK model versions, with guidance to use the native provider, instead of adding v4 compatibility? I'll hold off on further changes to #51482 until the intended scope is clear, and can revise or close it accordingly.

  5. rekram1-node commented on Oct 5, 2026

    @rekram1-node
    Collaborator

    btw we aren't going to support ai sdk v7, so if u rlly need to use aisdk I recommend you pin ur versions. We will try to only install v6 by default.

    I'm going to close this issue since v7 support definitely isnt in scope but I have some related prs ill merge

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

Metadata

Metadata

Assignees

No one assigned

    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