"thinking is enabled but reasoning_content is missing in assistant tool call message": Kimi K2 tool loops fail on the second turn
Kimi K2 models think before they call a tool, and Moonshot returns that thinking as reasoning_content on the assistant message. On the next turn Moonshot expects the same field back, so the model can see what it was thinking when it made the call. Most OpenAI-compatible clients were written for an API where assistant messages have only content and tool_calls, so they drop the field when they rebuild the history, and the second request fails with this 400. Every tool call in a session then dies on its first result, which looks like the model "not working" rather than a serialisation problem.
Last verified September 18, 2026 against earendil-works/pi #4251 (👍18), microsoft/vscode #312746: Copilot drops reasoning_content (👍15), zed-industries/zed #51743 (👍13), Moonshot docs: thinking and reasoning_content · 4 min read
| Provider | Moonshot AI (api.moonshot.ai) and hosts that reuse the Kimi chat template |
| HTTP status | 400 Bad Request |
| Message | thinking is enabled but reasoning_content is missing in assistant tool call message at index N |
| Models | Kimi K2.5, K2.6, K2.7 Code, K3 (thinking on) |
| When it happens | On the second turn of a tool loop: the client sends the conversation back with the assistant's tool call but without the reasoning_content that came with it |
| Who hits it | opencode and pi, GitHub Copilot custom endpoints, Zed, Goose, and hand-written loops that copy only content and tool_calls |
| Can you wait it out? | No. It is a validation rule, not capacity |
- Own code: copy
reasoning_contentonto the assistant message you replay. One line. - A tool you do not control: update it (pi and opencode closed their reports as fixed) or use a host that does not require the field back.
- Copilot custom endpoints dropped the field when the reports were filed (microsoft/vscode #312746); check your version, or use another host or model there.
What the error looks like
{
"error": {
"message": "thinking is enabled but reasoning_content is missing in assistant tool call message at index 3",
"type": "invalid_request_error"
}
}The index points at the assistant message in your messages array that carries tool_calls but no reasoning_content. A history that fails typically looks like this:
[
{"role": "user", "content": "Run the tests and fix what fails."},
{"role": "assistant", "content": null,
"tool_calls": [{"id": "call_1", "type": "function", "function": {"name": "exec", "arguments": "{\"cmd\":\"pytest -q\"}"}}]},
{"role": "tool", "tool_call_id": "call_1", "content": "2 failed, 8 passed"}
]Why Moonshot requires it
With thinking enabled, the model's reasoning is part of its context for the turn that follows. DeepSeek, GLM and MiniMax treat a missing reasoning_content as "no reasoning to show" and carry on. Moonshot treats it as an invalid message and refuses, which is stricter but not wrong: the alternative is a model that silently loses the plan it was following.
The same rule is why Copilot's custom-endpoint mode failed with Kimi and DeepSeek thinking models: it rebuilt the history from its own representation without the field (microsoft/vscode #312746, 👍15).
Fix 1: keep the field when you replay
msg = resp.choices[0].message
history.append({
"role": "assistant",
"content": msg.content,
"tool_calls": [tc.model_dump() for tc in msg.tool_calls],
"reasoning_content": getattr(msg, "reasoning_content", None), # keep it; Moonshot requires it back
})In streaming code, accumulate delta.reasoning_content the same way you accumulate delta.content, and store it on the message. If you use the OpenAI SDK, the field is preserved on the response object under model_extra; read it from there.
Fix 2: a host that does not require it
Hosts differ. On September 18, 2026 we replayed the history above, without reasoning_content, to each model we serve and asked for the tool result to be read back:
| Model | Replay without reasoning_content |
|---|---|
| Kimi K2.7 Code | Works |
| DeepSeek V4 Flash, V4.1 Flash, V4 Pro | Works |
| GLM 5.3, GLM 5.3 Flash | Works |
| MiniMax M3 | Works |
| GPT-OSS 120B | Works |
| Llama 3.3 70B | Works (does not reason) |
The full matrix, with streaming, tool_choice and JSON output, is on the model capabilities page.
Frequently asked questions
Can I just turn thinking off?
On Moonshot, yes: with thinking disabled the field is not required. You lose the reasoning, which for a coding agent is most of what you are paying Kimi for.
I send reasoning_content and still get the error.
Check the index in the message: one assistant message earlier in the history is probably missing it, often the first one, created before your fix.
Does the Responses API have this problem?
Only if the translation layer drops reasoning items. Ours keeps them, and Codex CLI sends them back on every turn.
Where Inference APIs fits
Kimi K2.7 Code runs here on a host that accepts the replayed tool call with or without reasoning_content. We tested the exact history above against every chat model we serve on September 18, 2026: all nine answered the second turn correctly. If the tool you use strips the field and you cannot change it, pointing it at our endpoint is a base URL change; the opencode and VS Code guides have the configs.
Something changed or wrong? Tell us and we will re-verify the entry.
