MoonshotAI / MoonshotAI/kimi-code
webp/gif read via built-in Read-image tool 400s on kimi-for-coding and permanently deadlocks the session (failed image re-sent every turn)
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 7.5k
- Forks
- 1.2k
- Avg merge
- 11h 53m
- Merged PRs (30d)
- 350
Description
Summary
A single webp (or gif) image that is pulled into the conversation by the built-in Read image file tool makes every subsequent LLM request fail with a non-retryable 400 ... failed to decode image, and because the failed image stays in history and is re-sent on every turn, the session is permanently deadlocked — no prompt can ever make progress again.
The image MIME is sniffed correctly (image/webp / image/gif), so this is not a mislabeling bug. The kimi-for-coding endpoint simply rejects these formats, and kimi-code has no guard, no transcode, and no way to drop the offending image from context.
Environment
- kimi-code 0.18.0 (observed). The
0.19.2changelog does not appear to address the failed-image / history-resend path, but I have not re-verified on 0.19.2. - Model:
kimi-for-coding(Kimi-k2.6) - macOS (arm64)
- Driven headlessly by an ACP client in
--yolomode (non-interactive), though the same mechanism applies to any harness.
What happens
The agent, during a normal task, calls the built-in Read image file tool on a local asset that happens to be a webp (saved with a misleading .png filename) and a small gif:
tool.result:
<system>Read image file. Mime type: image/webp. Size: 83184 bytes ...</system>
<image path=".../original_imgs/article2_img07.png">
image_url: blobref:image/webp;<hash>
The very next LLM request fails:
WARN llm request failed turnStep=2.26 attempt=1/3 model=kimi-for-coding
errorName=APIStatusError statusCode=400
errorMessage="400 Invalid request: prepare image failed error, status code: 400,
message: failed to decode image: invalid or unsupported image format"
ERROR turn failed turnId=2
WARN acp: turn ended with failed reason ... "retryable":false
From that point on, every turn fails immediately with the identical error:
turn failed turnId=2 (step 2.26, when the image entered context)
turn failed turnId=3
turn failed turnId=4
turn failed turnId=5 (sending "?" — anything — just triggers another doomed request)
The conversation contained 28 images total; 23 PNG + 2 JPEG decoded fine. The only failing inputs were 2 GIF + 1 WebP, all of them sniffed and labeled correctly as image/gif / image/webp.
Root cause (two compounding problems)
-
No format guard before sending to the vision endpoint. The Read-image tool forwards
image/webpandimage/giftokimi-for-coding, which returns400 failed to decode image. (Note: the general Kimi Vision docs list webp/gif as supported, butkimi-for-codingclearly does not accept them — an inconsistency worth confirming on the server side. Either way, kimi-code should transcode to png/jpeg, or refuse to attach, instead of poisoning the turn.) -
A non-retryable image error permanently wedges the session. Because the failed image remains in history and is re-sent on every following request, turns 3/4/5/... can never succeed — there is no recovery short of starting a brand-new session or hand-editing
wire.jsonl. This is the same family as #2279 (historical images re-sent) and #905 (no offload fallback), but here it is triggered by the built-in Read-image tool on a server-rejected format, not by a text-only model or a web-session restore.
Expected behavior
Any one of these would break the deadlock:
- Before attaching, check the active model's accepted formats and transcode webp/gif → png/jpeg (or downscale) when needed.
- If the endpoint returns a non-retryable image-decode 400, drop the offending image from context (replace with a text note like
[image omitted: unsupported format]) and let the turn proceed, instead of re-sending it forever. - Surface the failure to the user/harness as a recoverable condition rather than an infinite identical-error loop.
Steps to reproduce
- Put a
.png-named file that is actually webp bytes (any web-downloaded webp) in the working dir, plus a small.gif. - Ask the agent to read/analyze those images with
kimi-for-codingas the model. - Observe the
400 failed to decode image(non-retryable). - Send any further message → it fails immediately with the same error. The session never recovers.
Related
- MoonshotAI/kimi-cli#2279 — historical images repeatedly re-sent after session restore
- MoonshotAI/kimi-code#905 — pasted images fail on text-only models; should offload to file + MCP
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 with the built-in Read image file tool and follow how image attachments enter the LLM request and remain in conversation history. Reproduce with a GIF or WebP against kimi-for-coding, then inspect the retry and history-handling paths. Done means an unsupported image is transcoded or omitted and later turns proceed instead of repeating the non-retryable 400.
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
- 48/100