anomalyco / anomalyco/opencode
`OpenAIResponses.lowerMessages` strips reasoning item `id` when replaying reasoning, causing HTTP 400 `invalid_payload` on Azure/OpenAI Responses API
@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/azureor@ai-sdk/openainpm 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
- Configure a model with
reasoning: trueagainst a Responses-API-compatible provider (Azure OpenAI in our case). - Start a session and send a message that causes the model to reason
- 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.
- 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?: string → id: 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
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.