Skip to content

Responses: tool-call continuation intermittently fails with 400 "The reasoning_text in the thinking mode must be passed back to the API" #56776

Open

Description

@xeexee98

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/responses and to opencode.ai/inference/go/v1/responses (the latter needs x-opencode-session) in 6 mutated shapes — all returned 200: original, content text emptied, content removed, id only, encrypted_content replaced with a nonexistent UUID, and invalid encrypted_content + no content (exactly what the client emits on a full replay). A bogus previous_response_id with 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

  1. Upstream reasoning items carry the thinking plaintext in content: [{type: "reasoning_text", text}]; encrypted_content is not ciphertext but a reference of the form <response_id>-0 (38 chars, identical format from DeepSeek and from the OpenCode Go gateway).
  2. opencode persists {type, text, state: {itemId, reasoningEncryptedContent}}, but the outbound input[] entry is built as {type: "reasoning", id, summary: [], encrypted_content} — the plaintext content is never sent back. If the server-side reference is unavailable, there is no fallback, which is literally what the error message asks for.
  3. The responses path has a full ↔ incremental switch: mode: "incremental" sends previous_response_id plus new items only, and is chosen only when the new input prefix is item-identical to previous.request.input + previous.output. Anything that rewrites history (compaction / context-clipping plugin, or an earlier failed step) forces a mode: "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

  1. 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.
  2. If reference-based reasoning replay is inherently fragile for long sessions, would re-sending reasoning_text on 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

  1. opencode-agent commented on Oct 18, 2026

    @opencode-agent
    Contributor

    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 as id + summary (summary_text) + encrypted_content, never as content: [{type: "reasoning_text"}]. After a history rewrite, the client resends the whole history instead of only the new items with previous_response_id. That matches your theory: a fully replayed request depends on the server-side reference still being valid. Whether sending reasoning_text back avoids the error has to be tested against the real API.

    Related issues:

    If 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions