Repository navigation
Description
On the Responses channel, a tool-call step is occasionally followed by a failed continuation request:
AI.Error: The `reasoning_text` in the thinking mode must be passed back to the API. (request_id: ...)
at SessionStep.attempt (B:/~BUN/root/chunk-*.js:6:3619)
The tool call itself completes and its result is persisted; the assistant message is stored with finish: error. The failure is the request that continues the session after the tool call (Failed to drain Session), and it retries 3 times with the same body, all failing.
Frequency in one long session (3,807 stored messages): 7 failures / 599 tool-call continuations (~1.2%). Not tied to a specific tool — observed after edit (3), execute (6), shell, grep, websearch, webfetch, write.
It happens on both @opencode/ai/providers/openai/responses (OpenCode Go, https://opencode.ai/inference/go/v1, model deepseek-v4.1-flash, variant high) and on custom providers built on @opencode/ai/providers/openai-compatible/responses (https://api.deepseek.com, https://open.bigmodel.cn/api/v1).
request_ids from opencode.log (Failed to drain Session):
2026-10-15T17:49:08Z run=7b0b399b 5ddaa5ea-cb67-4e91-8fc1-9f76c16dcc94
2026-10-18T10:25:55Z run=a6ccaa93 750524db-3a6c-4806-bb62-d3eb07a9c62b
2026-10-18T10:27:03Z run=a6ccaa93 35f43596-9154-421e-9a0d-04240a63fbb2
2026-10-18T10:27:38Z run=a6ccaa93 4fed9b52-29db-4d5c-b499-9bf54e2d3f2c
2026-10-18T10:45:36Z run=089ee960 33df5d0a-528d-4c76-8a67-548909b7b4d5
2026-10-18T10:48:25Z run=69b49ba2 0ebc5e30-9b5a-4565-83b2-c1ca832a3c53
2026-10-18T11:48:12Z run=69b49ba2 7335d47e-9e53-481b-a0ac-f29b85906446
What I ruled out first
- Not the stored reasoning shape. Grouping every continuation by the preceding assistant's reasoning
state:{itemId, reasoningEncryptedContent}→ 317 OK / 6 failed,{itemId}only → 78 / 1, no reasoning at all → 204 / 1. - Not reproducible from request shape. I replayed stored reasoning items directly to
api.deepseek.com/responsesand toopencode.ai/inference/go/v1/responses(the latter needsx-opencode-session) in 6 mutated shapes — all returned 200: original,contenttext emptied,contentremoved,idonly,encrypted_contentreplaced with a nonexistent UUID, and invalidencrypted_content+ nocontent(exactly what the client emits on a full replay). A bogusprevious_response_idwith incremental input also returned 200. - So with short histories the error cannot be triggered by an invalid request body, which is why I'm filing this without a minimal repro.
Observations that may matter
- Upstream reasoning items carry the thinking plaintext in
content: [{type: "reasoning_text", text}];encrypted_contentis not ciphertext but a reference of the form<response_id>-0(38 chars, identical format from DeepSeek and from the OpenCode Go gateway). - opencode persists
{type, text, state: {itemId, reasoningEncryptedContent}}, but the outboundinput[]entry is built as{type: "reasoning", id, summary: [], encrypted_content}— the plaintextcontentis never sent back. If the server-side reference is unavailable, there is no fallback, which is literally what the error message asks for. - The responses path has a full ↔ incremental switch:
mode: "incremental"sendsprevious_response_idplus new items only, and is chosen only when the newinputprefix is item-identical toprevious.request.input + previous.output. Anything that rewrites history (compaction / context-clipping plugin, or an earlier failed step) forces amode: "full"replay of the whole history including all reasoning items. Our failures cluster after history rewrites, but I have no request body to prove causality.
Ask
- Can the
request_ids above be looked up server-side? The client side is largely ruled out, so knowing why the reference was rejected would close this. - If reference-based reasoning replay is inherently fragile for long sessions, would re-sending
reasoning_texton replay (making the request self-contained) be an acceptable fix?
Plugins: local context-compaction plugin (unpublished, wraps session.prompt and prunes history between steps) — but the same 400 also occurred on continuations where it did not touch the turn.
OpenCode version: 2.0.32
OS: Windows 10 (build 19045)
Activity
opencode-agent commentedon Oct 18, 2026
Thanks for the detailed report and for ruling so much out yourself.
We can't reproduce this here. The 400 comes from the provider (DeepSeek, or the OpenCode Go gateway), only on roughly 1 in 85 continuations in long sessions, and our sandbox has no provider accounts. As you found, replaying the request shapes on their own returns 200. So this needs a maintainer who can look up the request IDs you listed on the server side.
In the 2.0.32 code (
packages/ai/src/protocols/open-responses.ts,lowerReasoning), earlier reasoning is sent back asid+summary(summary_text) +encrypted_content, never ascontent: [{type: "reasoning_text"}]. After a history rewrite, the client resends the whole history instead of only the new items withprevious_response_id. That matches your theory: a fully replayed request depends on the server-side reference still being valid. Whether sendingreasoning_textback avoids the error has to be tested against the real API.Related issues:
reasoning_text must be passed back400 #55584: the samereasoning_text must be passed back400 ondeepseek-v4.1-flash, closed earlier for lack of a CLI-only report (yours is one)reasoning_contenton the chat-completions path through Go/v1/responses400 on Go/Zen for valid item sequencesIf you can capture the outgoing request body of a failing continuation (redacted), especially whether it was a full replay and how old the reasoning references in it are, please add it here.