Repository navigation
reasoning_content stripped from assistant messages on replay, causing KV cache invalidation on local inference #19081
Description
Description
When an assistant response contains reasoning_content (thinking tokens), OpenCode correctly handles it for the immediate turn. However, on subsequent turns, when that assistant message is replayed as part of the conversation history, the thinking block is stripped.
This silently removes tokens from the conversation history, which invalidates the KV cache on local inference backends (like llamacpp) at the point where the thinking block used to be. The backend is then forced to reprocess the entire context from that turn forward on every subsequent turn.
While strict providers like Kimi K2.5 hard-fail with a 400 error when this happens #10996 , permissive local backends suffer a "silent fail." This makes thinking models non-interactive on local setups due to the massive latency penalty of constant prompt reprocessing. This might even be present when some providers for some models, which does result in less cached prompts, resulting in added costs.
This issue does not need reprocessing when the model is in a turn with itself, making calls back and forth.
Plugins
No response
OpenCode version
1.2.27
Steps to reproduce
Setup: NixOS, OpenCode communicating with a local llama-server hosting a Qwen3.5-122B model. Qwen3.5 35B, GPT-OSS-120B/20B, were also tested and gave same results of stripping which resulted in invalidation. All Qwen3.5 models, regardless if thinking or not result in the same problem (non thinking still produced empty <think>\n\n</think> block which is also stripped and requires reprocessing.
Prompt 1: Ask the model to "Say Hello". This initializes the system prompt and tools. Context is cached normally.
Prompt 2: Ask the model to do a task (e.g., "Read a file"). The model outputs a thinking block followed by the tool call/action. It also works with normal output, but reading the can be done quicker than genereting 10k tokens.
Prompt 3: Say "Hello" again. OpenCode sends the conversation history, but strips the thinking block from the Prompt 2 assistant message. The llama-server KV cache is invalidated at the Prompt 2 boundary, forcing it to fully reprocess the output of Prompt 2.
Prompt 4: Say "Hello" again. The same thing happens; it has to reprocess the output of Prompt 3.
Screenshot and/or share link
When Prompt 2 completes (tool_call assistant message includes thinking):
<|im_start|>assistant
<think>
The user wants me to read PROJECT_PLAN.md and nothing else, then reply with "Done". Let me read this file.
</think>
<tool_call>
<function=read>
<parameter=filePath>
/home/delgon/ai-controller/PROJECT_PLAN.md
</parameter>
</function>
</tool_call><|im_end|>
When Prompt 3 replays the same message (thinking block stripped):
<|im_start|>assistant
<tool_call>
<function=read>
<parameter=filePath>
/home/delgon/ai-controller/PROJECT_PLAN.md
</parameter>
</function>
</tool_call><|im_end|>
Token counts from llama-server logs
| Request | Task | Tokens Sent | Tokens Processed | Cache Behavior |
|---|---|---|---|---|
| Prompt 1 ("Hello") | 269 | 18,006 | 18,006 | Fresh start |
| Prompt 2a (tool call) | 312 | 18,048 | 46 | Not perfect cache hit (sim=0.998) |
| Prompt 2b (tool result) | 384 | 37,013 | 18,896 | Perfect cache hit (f_keep=1.000) |
| Prompt 3 ("Hello again") | 424 | 36,995 | 18,993 | Cache invalidated at token ~18,002 |
Prompt 3 sends 18 fewer total tokens than Prompt 2b despite having two additional messages (the "Done" response + user's Prompt 3). The missing tokens correspond exactly to the stripped <think> block.
llama-server cache rollback log (Prompt 3)
slot update_slots: id 0 | task 424 | n_past = 18046, slot.prompt.tokens.size() = 37045
slot update_slots: id 0 | task 424 | Checking checkpoint with [37008, 37008] against 18046...
slot update_slots: id 0 | task 424 | Checking checkpoint with [18001, 18001] against 18046...
slot update_slots: id 0 | task 424 | restored context checkpoint (pos_min = 18001, n_past = 18002)
slot update_slots: id 0 | task 424 | erased invalidated context checkpoint (pos_min = 26308, ...)
slot update_slots: id 0 | task 424 | erased invalidated context checkpoint (pos_min = 34500, ...)
slot update_slots: id 0 | task 424 | erased invalidated context checkpoint (pos_min = 35984, ...)
slot update_slots: id 0 | task 424 | erased invalidated context checkpoint (pos_min = 37008, ...)
prompt eval time = 75166.29 ms / 18993 tokens
The cache diverges at the tool call boundary (~token 18,002) and must reprocess everything after it.
Operating System
NixOS
Terminal
No response
Activity
github-actions commentedon Mar 25, 2026
This issue might be a duplicate of existing issues. Please check:
reasoning_contentis silently stripped from assistant messages on replay for non-Claude models, causing the upstream API to receive incomplete conversation history. Your issue describes the same bug with a different (and well-documented) downstream symptom: KV cache invalidation on local inference backends (llama.cpp).feat(model): add preserveInterruptedResponse option for KV cache cons…
vpavlin commentedon Apr 2, 2026
Bump! I tried OpenCode today and got the same experience.
server: fix reasoning item content format handling for multi-turn
KingingWang commentedon Apr 5, 2026
I have encountered the same issue
server: fix reasoning item content format handling for multi-turn
0x62ash commentedon Apr 16, 2026
I constantly have this issue on Qwen3.5.
I tried Qwen3.6 with --chat-template-kwargs {"preserve_thinking": true}, and the problem still persists.
0x62ash commentedon Apr 17, 2026
ggml-org/llama.cpp#19394 (comment)
server: fix reasoning item content format handling for multi-turn
server: fix reasoning item content format handling for multi-turn
slepkaviba commentedon May 2, 2026
I noticed woth Qwen3.6 and preserved thinking it works well.
For me the issue was with opencode-agent-memory or so plugin. Disabling it made context super stable.
1 remaining item
server: fix reasoning item content format handling for multi-turn
ar7max commentedon May 14, 2026
Yep, same
server: fix reasoning item content format handling for multi-turn
nilo85 commentedon May 19, 2026
I seem to have created a duplcate of this issue #28333
Closing my issue in favor of this one, see my duplicate issue for my context if it helps =)
nilo85 commentedon May 19, 2026
After making a build with my PR #28352 it works like a charm! 🥳
server: fix reasoning item content format handling for multi-turn
hcwhan commentedon May 26, 2026
Such an obvious problem — why does it feel impossible to move forward after all this time? I’ve seen 3 PRs with no progress.
Does opencode not pay much attention to local LLM?
nilo85 commentedon May 26, 2026
Seeing that they have about 1000 open pull requests, it's an agentic coder product, people probably post craplot of vibed stuff that does not meet standards or roadmap, I bet they are quite overwhelmed and things needs to take time.
Checkout the latest release tag, ask your LLM to apply my PR and use it now already if it's blocking you :)
CWaterSon commentedon Jun 25, 2026
Hi, Is there a chance to fix this bug (quickly)?
nilo85 commentedon Jun 25, 2026
@rekram1-node
Any chance we can look at my pr #28352 which should fix the issue? I have been patient (and I understand the shear volume of PRs you get is in itself problematic)
But the lack of reaction, to a fix to a proper bug is kind of demotivating =)
PS: alse reconsider if #28346 should be merged (was auto closed, but AFAIK once you enable native LLM api this bug will reapear if left alone)
github-actions commentedon Aug 25, 2026
To stay organized issues are automatically closed after 60 days of no activity. If the issue is still relevant please open a new one.