anomalyco / anomalyco/opencode

`OpenAIResponses.lowerMessages` strips reasoning item `id` when replaying reasoning, causing HTTP 400 `invalid_payload` on Azure/OpenAI Responses API

Open
#49,574 1 comment 4 reactions 1 assignee View on GitHub

@neriousy is already working on this.

Since Sep 17, 2026.

Dominant language
TypeScript
Stars
209k
Forks
27.5k
PR merge metrics
PR metrics pending

Description

Description
Summary

When OpenCode talks to a reasoning model over the OpenAI Responses API and the model returns a reasoning item, OpenCode's native Responses protocol implementation (packages/llm/src/protocols/openai-responses.ts) constructs the replayed reasoning item on the next turn (e.g. after a tool call) without its id. Azure/OpenAI's Responses API now rejects the resulting id-less reasoning item with:

HTTP 400 invalid_request_error / invalid_payload
"The provided data does not match the expected schema"

This breaks every multi-turn tool-call conversation for any provider whose reasoning output includes encrypted content.

Environment
  • OpenCode version: 1.18.31
  • Provider: Azure OpenAI, via a gateway proxying the OpenAI Responses API, using the @ai-sdk/azure or @ai-sdk/openai npm provider
  • Model: a GPT-5-class model with reasoning: true
Root cause

File: packages/llm/src/protocols/openai-responses.ts, function lowerMessages (Effect.fn("OpenAIResponses.lowerMessages")).

Introduced in PR #34027 ("fix(llm): omit stateless response item ids", merged 26 Jun 2026). When replaying a reasoning item on a later turn, the object built and sent back to the API omits id entirely:

const replay = {
  type: reasoning.type,
  summary: reasoning.summary,
  encrypted_content: reasoning.encrypted_content,
}
reasoningItems[reasoning.id] = replay
input.push(replay)

The resulting type: "reasoning" item carries encrypted_content and summary, but no id — which Azure/OpenAI's backend now rejects (see Evidence below for the exact id/summary combinations tested).

Steps to reproduce
  1. Configure a model with reasoning: true against a Responses-API-compatible provider (Azure OpenAI in our case).
  2. Start a session and send a message that causes the model to reason
  3. Follow up with a prompt that has a tool call in it. OpenCode sends the follow-up request with the tool result, replaying the prior reasoning item.
  4. The follow-up request fails with HTTP 400 because the replayed reasoning item is missing its id.
Expected behavior

A replayed reasoning item should retain its original id alongside encrypted_content and summary — the three need to travel together as a matched pair for the backend to accept the item.

Suggested fix

File: packages/llm/src/protocols/openai-responses.ts, in lowerMessages, where the replay object is constructed and stored/pushed:

// Current — id omitted:
const replay = {
  type: reasoning.type,
  summary: reasoning.summary,
  encrypted_content: reasoning.encrypted_content,
}
reasoningItems[reasoning.id] = replay
input.push(replay)
// Fixed — id restored, so it travels with encrypted_content as a matched pair:
const replay = {
  type: reasoning.type,
  id: reasoning.id,
  summary: reasoning.summary,
  encrypted_content: reasoning.encrypted_content,
}
reasoningItems[reasoning.id] = replay
input.push(replay)

This also requires reverting the schema/type change from PR #34027 that made id optional back to required on this item — OpenAIResponsesReasoningItem's id: Schema.optionalKey(Schema.String)id: Schema.String, and OpenAIResponsesReasoningInput's id?: stringid: string — so the type system reflects that a replayed reasoning item always carries its id again.

Plus a regression test in packages/llm/test/provider/openai-responses.test.ts asserting the input array's reasoning item includes id: reasoning.id after a store: false-style replay (e.g. adapting the existing "preserves reasoning items keyed by the copilot namespace instead of dropping them" test pattern from PR #34027's own test suite, but asserting id is present rather than absent).

Happy to open a PR with this fix and test if that's useful — let me know.

Plugins

No response

OpenCode version

1.18.31

Screenshot and/or share link

No response

Operating System

MacOS 26.7

Terminal

Warp

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.