You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Compaction produced no summary when the conversation ends on a tool result #53242
Auto-compaction often fails with "Compaction produced no summary" when using a models with tooling. I can only reproduce when the conversation ends on a tool result.
The summary request still includes the session's tools. The model then writes its summary in reasoning_content, makes a tool call, and returns empty content. OpenCode only reads content, so the summary is lost.
Plugins
None
OpenCode version
v2.0.22
Steps to reproduce
Configure a provider pointing at llama.cpp using a Qwen3-based model. Use the reasoning_content config.
Start a session that runs many tool calls in a row (e.g. Playwright MCP) until it reaches the compaction threshold.
Auto-compaction fires while the last message is a tool result.
I'd like to take this. The summary request still sends the session's tools, so the model can answer with a tool call and empty text. I'll set toolChoice: "none" on the summary request in session/compaction.ts and add a test.
Thanks for the clear report. I reproduced this on v2.0.22, and it still happens on the latest dev (fd8268e).
There were no real model credentials here, so I used a mock llama.cpp-style OpenAI-compatible server for the model's side of what you describe. The provider is set up like yours: @ai-sdk/openai-compatible with interleaved: { field: "reasoning_content" } and a small context limit.
On normal turns the mock makes a shell tool call and reports a prompt near the context limit.
When it gets the summary request, it writes the summary in reasoning_content, makes a tool call, and returns empty content.
Auto-compaction fires right after a tool result. The summary request and the follow-up reminder both still carry all of the session's tools, and only text content is read back. The run ends with Error: Compaction produced no summary. The script below reproduces it against either the installed release or a source checkout.
Reproduction script (repro.sh)
#!/usr/bin/env bash# Reproduces #53242: auto-compaction fails with "Compaction produced no summary" when the# model (llama.cpp/Qwen3 style) answers the summary request with reasoning_content + a tool call.# Usage: ./repro.sh (uses `opencode` on PATH)# OC_DEV=/path/to/opencode-checkout ./repro.sh (runs from source)set -euo pipefail
DIR=$(mktemp -d);cd"$DIR"
cat > mock.ts <<'TS'Bun.serve({ port: 8089, async fetch(req) { const body: any = await req.json() const msgs = body.messages const last = msgs.at(-1) const lastText = typeof last.content === "string" ? last.content : JSON.stringify(last.content) const isSummary = last.role === "user" && /## Objective|summary template/.test(lastText) const names = (body.tools ?? []).map((t: any) => t.function.name) const tool = names.find((n: string) => /bash|shell/.test(n)) ?? "bash" console.error(`[mock] ${msgs.length} msgs, roles=${msgs.map((m: any) => m.role).join(">")}, tools=${names.length}, summaryRequest=${isSummary}`) const id = "c" + Math.random() const chunk = (delta: any, finish: string | null = null, usage?: any) => `data: ${JSON.stringify({ id, object: "chat.completion.chunk", created: 0, model: body.model, choices: [{ index: 0, delta, finish_reason: finish }], ...(usage ? { usage } : {}) })}\n\n` const call = () => ({ tool_calls: [{ index: 0, id: "call_" + Math.floor(Math.random() * 1e9), type: "function", function: { name: tool, arguments: JSON.stringify({ command: "echo step", description: "step" }) } }] }) let out = "" if (isSummary) { // What the report describes: summary in reasoning_content, then a tool call, empty content. out += chunk({ role: "assistant", reasoning_content: "## Objective\nKeep browsing.\n\n## Next Move\nContinue.\n" }) out += chunk(call()) out += chunk({}, "tool_calls", { prompt_tokens: 7000, completion_tokens: 50, total_tokens: 7050 }) } else { // Normal turn: a tool call, with a prompt size close to the 8192 context limit. out += chunk({ role: "assistant", reasoning_content: "Need another tool call." }) out += chunk(call()) out += chunk({}, "tool_calls", { prompt_tokens: 7900, completion_tokens: 20, total_tokens: 7920 }) } return new Response(out + "data: [DONE]\n\n", { headers: { "content-type": "text/event-stream" } }) },})TS
bun mock.ts & MOCK=$!;trap'kill $MOCK' EXIT; sleep 1
mkdir proj &&cd proj && git init -q
cat > opencode.json <<'JSON'{ "$schema": "https://opencode.ai/config.json", "model": "llama.cpp/qwen3", "permission": { "bash": "allow", "shell": "allow" }, "provider": { "llama.cpp": { "npm": "@ai-sdk/openai-compatible", "options": { "baseURL": "http://127.0.0.1:8089/v1", "apiKey": "x" }, "models": { "qwen3": { "name": "Qwen3", "reasoning": true, "interleaved": { "field": "reasoning_content" }, "limit": { "context": 8192, "output": 1024 } } } } }}JSONif [ -n"${OC_DEV:-}" ];thenexport BUN_OPTIONS="--conditions=browser --preload=$OC_DEV/packages/tui/node_modules/@opentui/solid/scripts/preload.js"; timeout 120 bun --conditions=browser --preload="$OC_DEV/packages/tui/node_modules/@opentui/solid/scripts/preload.js""$OC_DEV/packages/cli/src/index.ts" run "browse around"2>&1||trueelse
opencode --version; timeout 120 opencode run "browse around"2>&1||truefi
Another data point, with a variant that the proposed toolChoice: "none" fix may not cover: no tool call at all, only reasoning.
Setup: opencode 2.2.2 on macOS, an OpenAI-compatible provider (a local gateway in front of oMLX 0.7.2, which serves Qwen3.8-Flash-Next through the Splash engine), reasoning returned as reasoning_content, effort xhigh.
Auto-compaction fired right after a tool result, at about 207k tokens of a 256k window.
The summary request ended with finish_reason: stop after 3,223 tokens, 3,222 of them reasoning, and no text at all.
The retry ("The previous response did not fill in the required summary template...") did the same: 1,578 tokens, 1,577 of them reasoning.
Result: Compaction produced no summary. No output limit was involved (about 48k of output budget was left), and the server logged no error.
Two earlier compactions in the same session also followed tool results and produced normal summaries, so it is intermittent. The model seems to write the summary inside its thinking and then stop. Sending the session a user message ("Please continue.") made the next compaction succeed.
Since only text is read back, it might help to also handle a reply that is all reasoning: for example, retry with thinking off (or at low effort) and without tools, or use the reasoning text when the reply has no content.
Activity
Shalin-Shah-2002 commentedon Oct 5, 2026
I'd like to take this. The summary request still sends the session's tools, so the model can answer with a tool call and empty text. I'll set
toolChoice: "none"on the summary request insession/compaction.tsand add a test.opencodejr commentedon Oct 27, 2026
Thanks for the clear report. I reproduced this on v2.0.22, and it still happens on the latest
dev(fd8268e).There were no real model credentials here, so I used a mock llama.cpp-style OpenAI-compatible server for the model's side of what you describe. The provider is set up like yours:
@ai-sdk/openai-compatiblewithinterleaved: { field: "reasoning_content" }and a small context limit.reasoning_content, makes a tool call, and returns empty content.Auto-compaction fires right after a tool result. The summary request and the follow-up reminder both still carry all of the session's tools, and only text content is read back. The run ends with
Error: Compaction produced no summary. The script below reproduces it against either the installed release or a source checkout.Reproduction script (repro.sh)
Run it:
bash repro.shinfinitr0us commentedon Oct 30, 2026
Another data point, with a variant that the proposed
toolChoice: "none"fix may not cover: no tool call at all, only reasoning.Setup: opencode 2.2.2 on macOS, an OpenAI-compatible provider (a local gateway in front of oMLX 0.7.2, which serves Qwen3.8-Flash-Next through the Splash engine), reasoning returned as
reasoning_content, effort xhigh.finish_reason: stopafter 3,223 tokens, 3,222 of them reasoning, and no text at all.Compaction produced no summary. No output limit was involved (about 48k of output budget was left), and the server logged no error.Two earlier compactions in the same session also followed tool results and produced normal summaries, so it is intermittent. The model seems to write the summary inside its thinking and then stop. Sending the session a user message ("Please continue.") made the next compaction succeed.
Since only text is read back, it might help to also handle a reply that is all reasoning: for example, retry with thinking off (or at low effort) and without tools, or use the reasoning text when the reply has no content.