Skip to content

xAI: undersized images persist in history and prevent session recovery #54325

Open

Description

@batteryshark

OpenCode 2.0.25 with xAI Grok 4.7 rejects a tool-result image whose width or height is below 8 pixels. A read of a 6x8 PNG produces:

invalid_image: Image dimensions 6x8 are too small. Both width and height must be at least 8 pixels.

The image is persisted in the session transcript. Further messages include the same image and fail before the model can respond to instructions to resize it. Resizing the original file also leaves the persisted bytes unchanged.

Reproduction:

  1. Use an xAI vision model.
  2. Have the read tool read a 6x8 PNG.
  3. Send another message asking the agent to recover.

Expected: omit incompatible inline media with an explanatory text part, or normalize it before sending, including when replaying saved history. Preserve the source file and transcript.

The request assembly already filters unsupported xAI image formats in packages/core/src/session/model-request.ts, but has no minimum-dimension check. This is distinct from the GIF issue #53786. The same problem applies to JPEG and WebP images with a short side below 8 pixels.

Activity

  1. opencode-agent commented on Oct 10, 2026

    @opencode-agent
    Contributor

    Thanks for the clear report. I could confirm this on 2.0.25 and it still happens on the latest v2 commit (4617210).

    There are no xAI credentials here, so I tested the code that builds each request from the saved session history. A real 6x8 PNG returned by the read tool and stored in the history comes out unchanged and is still in the request for provider xai, on every later turn. The current xAI check in packages/core/src/session/model-request.ts only filters by format (so GIFs are handled, as in #53786). Nothing checks image width or height, so xAI's invalid_image ... too small error repeats on every turn, as you describe.

    The script below reproduces it. Related:

    Reproduction script (xai-small-image.test.ts)
    // Reproduction for anomalyco/opencode#54325.
    // Run from an opencode v2 checkout:
    //   cp xai-small-image.test.ts <repo>/packages/core/test/ && cd <repo>/packages/core && bun test test/xai-small-image.test.ts
    //
    // A 6x8 PNG (xAI requires both sides >= 8px) returned by the read tool and stored in the
    // session history is passed unchanged to the xAI request on every later turn, so xAI
    // answers each turn with 400 invalid_image and the session cannot recover.
    import { expect, test } from "bun:test"
    import { Message, ToolResultPart } from "@opencode/ai"
    import { boundImages, unsupportedParts } from "@opencode/core/session/model-request"
    
    // Real 6x8 RGB PNG
    const PNG_6x8 =
      "iVBORw0KGgoAAAANSUhEUgAAAAYAAAAICAIAAABVpBlvAAAAEklEQVR4nGP4z8CAhtD5AyIEAGL6L9EoaAtUAAAAAElFTkSuQmCC"
    
    test("#54325: undersized image in saved history is still sent to xAI", () => {
      const history = [
        Message.user([Message.text("Read tiny.png")]),
        Message.tool(
          ToolResultPart.make({
            id: "call_1",
            name: "read",
            result: {
              type: "content",
              value: [
                { type: "text", text: "Image read successfully" },
                { type: "file", uri: `data:image/png;base64,${PNG_6x8}`, mime: "image/png", name: "tiny.png" },
              ],
            },
          }),
        ),
        Message.user([Message.text("Please recover: resize the image")]),
      ]
      const capabilities = { tools: true, input: ["text", "image"], output: ["text"] }
    
      // Same pipeline the request assembly uses: boundImages(unsupportedParts(..., "xai"))
      const sent = boundImages(unsupportedParts(history, capabilities, "xai"))
    
      const result = sent[1]?.content[0] as any
      const file = result.result.value.find((item: any) => item.type === "file")
      console.log("tool-result parts sent to xAI:", JSON.stringify(result.result.value.map((i: any) => i.type)))
      // BUG: the 6x8 image is still included; xAI rejects it with
      // "invalid_image: Image dimensions 6x8 are too small. Both width and height must be at least 8 pixels."
      expect(file).toBeDefined()
      expect(file.uri).toBe(`data:image/png;base64,${PNG_6x8}`)
    })

    Run it: bun xai-small-image.test.ts

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

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions