Skip to content

Images in chat history break text-only model sessions (GLM-5.2 and other non-vision providers) #755

Description

@Alexander-J-Quant

Problem

When jcode takes a screenshot or otherwise captures an image during a session, that image is stored as a ContentBlock::Image in the message history. On every subsequent turn, the full message history (including image blocks) is replayed to the provider.

For vision-capable models this is fine. But for text-only models like GLM-5.2 (via Z.AI OpenAI-compatible endpoint), the image content is sent to an API that cannot accept it. The request fails, the chat is permanently broken, and there is no in-TUI way to recover. The only option is to start a new session, losing all conversation context.

This happens even when the system prompt explicitly tells the agent not to capture screenshots. Tool calls that produce images (browser screenshot, computer_use, etc.) still inject ContentBlock::Image into history, and the provider has no way to handle it.

Reproduction

  1. Configure jcode with a text-only model (e.g. GLM-5.2 via Z.AI coding endpoint)
  2. In a session, trigger any action that produces an image in the message history (screenshot tool, pasted image, browser automation screenshot, etc.)
  3. Send another prompt

Expected behavior

The session continues working. Either:

  • (A) Image blocks are automatically stripped from the request payload when the active provider does not support vision (the Provider trait already exposes supports_image_input() for this), OR
  • (B) The user can remove individual images from the chat history via the TUI (hover/click an X on the image, or a /remove-image command), so the next request no longer contains them.

Ideally both. (A) prevents the problem. (B) gives the user manual control.

Actual behavior

The API request includes the image block. The text-only provider rejects it (400 error or similar). The session is stuck. Every subsequent prompt re-sends the same broken history. There is no way to delete the offending image without abandoning the session.

Environment

  • jcode v0.66.0
  • Provider: Z.AI GLM-5.2 (OpenAI-compatible, api.z.ai/api/coding/paas/v4)
  • macOS 26.5.1, Apple Silicon

Relevant code

  • ContentBlock::Image variant in crates/jcode-message-types/src/lib.rs
  • Provider::supports_image_input() already exists on the trait but is not consulted before replaying image blocks
  • Message serialization for provider requests happens in the provider runtime crates (e.g. jcode-provider-openai-runtime, jcode-provider-openrouter-runtime)

Proposed approach

Option A (automatic stripping) would be the least invasive fix. In the request-building path for each provider, filter out ContentBlock::Image when supports_image_input() == false. This is a one-line guard per provider message serialization.

Option B (manual deletion) requires TUI work: a hover-to-reveal delete affordance on rendered images, or a slash command, that removes the image block from the persisted session history.

Metadata

Metadata

Assignees

No one assigned

    Labels

    autonomous: noNeeds your brain: a product/design decision is required before anyone acts.bugSomething isn't workingpriority: highP1 - important bug or impactful feature, fix soontriage: needs-decisionNeeds maintainer decision/design thought

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions