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
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 withcapabilities = [ "thinking", "tool_use" ](no vision), used as[secondary_model]
Steps to reproduce
- Work in a session whose history contains image blocks (e.g. results of the built-in media-read tool, or pasted images).
- Switch the session model to the text-only model (
/model), or route a turn to it as the configured secondary model. - 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
/modelswitch 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.jsonlto unbrick affected sessions.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- 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