MoonshotAI / MoonshotAI/kimi-code

400 "unknown variant `image_url`, expected `text`" from text-only OpenAI-compatible providers when history contains images — poisons every subsequent turn

Open
#2,669 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
TypeScript
Stars
7.5k
Forks
1.2k
Avg merge
11h 53m
Merged PRs (30d)
350

Description

Environment

  • Kimi Code CLI 0.33.0, Linux (WSL2, Ubuntu 24.04)
  • Custom OpenAI-compatible provider in config.toml: type = "openai", base_url = "https://api.deepseek.com/v1", model declared with capabilities = [ "thinking", "tool_use" ] (no vision), used as [secondary_model]

Steps to reproduce

  1. Work in a session whose history contains image blocks (e.g. results of the built-in media-read tool, or pasted images).
  2. Switch the session model to the text-only model (/model), or route a turn to it as the configured secondary model.
  3. Send any message.

Actual behavior

Every request fails with:

[provider.api_error] 400 Failed to deserialize the JSON body into the target type:
messages[119]: unknown variant `image_url`, expected `text`

Kimi Code replays the history verbatim, so the provider (whose chat-completions schema only accepts text content parts for this model) rejects the whole payload. Because the image block stays in history, every subsequent turn fails the same way — the session is effectively bricked until the wire file is hand-patched.

Expected behavior

When the resolved model has no vision capability (Kimi Code already knows the capability set from the model declaration / catalog), image (and other non-text) content parts should be stripped or converted to a text placeholder before the request is sent, instead of being forwarded verbatim.

Notes

  • Same failure class as #1082 (failed image re-sent every turn deadlocks the session) and related to #905 (pasted images on text-only models) — but here the trigger is history replay to a custom OpenAI-compatible text-only provider via /model switch or secondary-model routing, and the error surfaces as a provider-side deserialization 400.
  • Current workarounds on our side: only run text-only models in fresh sessions without images, and a maintenance script that rewrites image blocks to text in wire.jsonl to unbrick affected sessions.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start at the /model switch and secondary-model routing, then trace how image-containing history from wire.jsonl is replayed to a resolved model without vision. Reproduce the provider-side deserialization failure and verify that subsequent turns no longer resend unsupported image parts; the fix should preserve usable text history for text-only providers.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
api, cli
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
52/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.