Description
On the stock OpenAI Responses path (@ai-sdk/openai, not the bundled openai-compatible / copilot adapter), a multi-turn GPT-5 session can silently jump back to an older task instead of replying to the most recent user message.
This does not always throw an API error. Sometimes the next assistant turn just continues stale context.
This looks related to the existing store:false / Responses replay bug family (#7724, #2941, #4426, #4445, #8746), but the symptom here is different: instead of rs_* not found, the model continues an older thread.
This also appears different from #17926. That issue is about the bundled Responses adapter for @ai-sdk/openai-compatible; this repro is on the stock OpenAI path.
Concrete example
I debugged a local failing session where:
- the user sends
commit
- the assistant correctly starts commit-related work
- on the next assistant turn, the model switches back to an older task/topic instead of continuing the commit flow
In this repro, the wrong assistant message still has the correct latest parentID, so the session/message ordering appears correct. The stale jump seems to be caused by replayed model context, not by selecting the wrong session/message.
Corrected hypothesis
My original hypothesis was too broad: it treated reasoning.encrypted_content in a store:false request as suspicious by itself.
That is probably wrong.
OpenAI's reasoning docs say that in stateless mode (store:false or zero data retention), clients must still retain and resend reasoning items, and must request reasoning.encrypted_content via include so those reasoning items can be replayed on future requests.
So the bug is not simply "encrypted_content exists in the request". For current tool-call follow-ups and current-turn continuation, encrypted_content is expected and needed.
The likely bug is narrower:
- stale reasoning from assistant turns before the latest user message may be replayed into a new request
- replay item IDs and encrypted reasoning state have different semantics and should not be treated as the same thing
- request/message normalization should remove stale reasoning before the latest user turn while preserving relevant/current reasoning for valid Responses continuation
In other words: keep hidden reasoning where OpenAI requires it, but do not replay stale hidden reasoning from older tasks into a new user turn.
Relevant code paths
The OpenAI request path in packages/opencode/src/provider/provider.ts sanitizes response item IDs before sending the final POST body:
if (model.api.npm === "@ai-sdk/openai" && opts.body && opts.method === "POST") {
const body = JSON.parse(opts.body as string)
const isAzure = model.providerID.includes("azure")
const keepIds = isAzure && body.store === true
if (!keepIds && Array.isArray(body.input)) {
for (const item of body.input) {
if ("id" in item) {
delete item.id
}
}
opts.body = JSON.stringify(body)
}
}
The safer layer for stale reasoning handling appears to be message normalization before the SDK converts model messages into Responses input: remove old assistant reasoning parts before the latest user message for @ai-sdk/openai + store:false, but preserve latest-turn reasoning and valid tool-call continuation.
Likely fix direction
A fix should probably:
- strip OpenAI response item IDs where needed for stateless replay compatibility
- drop stale assistant reasoning parts before the latest user turn on
store:false
- preserve latest/current reasoning so GPT-5 Responses can continue tool-call flows correctly
- avoid orphaning following tool results when stripping reasoning would otherwise remove an assistant message entirely
- add regression coverage at both the transform/message-normalization layer and the final OpenAI Responses request shape
This matches OpenAI's stateless Responses guidance better than treating all encrypted reasoning as stale.
Steps to reproduce
I do not have a tiny deterministic repro yet, but the pattern is:
- Use stock OpenAI GPT-5 on the Responses path
- Ensure requests are using
store:false or equivalent zero-retention behavior
- Let the assistant perform at least one tool-using task
- Send a short follow-up that changes task, for example
commit
- Occasionally, after the tool step, the next assistant turn resumes an older task/thread instead of the latest user request
Expected behavior
The assistant should continue the latest user request.
Actual behavior
The assistant sometimes replies to or continues an older task with no API error.
Additional context
I saw this on a local repro session where the stale turn resumed an earlier topic ("Clarifying code parameters") immediately after the user sent commit.
Related broader stale-context issues:
Description
On the stock OpenAI Responses path (
@ai-sdk/openai, not the bundledopenai-compatible/ copilot adapter), a multi-turn GPT-5 session can silently jump back to an older task instead of replying to the most recent user message.This does not always throw an API error. Sometimes the next assistant turn just continues stale context.
This looks related to the existing
store:false/ Responses replay bug family (#7724, #2941, #4426, #4445, #8746), but the symptom here is different: instead ofrs_* not found, the model continues an older thread.This also appears different from #17926. That issue is about the bundled Responses adapter for
@ai-sdk/openai-compatible; this repro is on the stock OpenAI path.Concrete example
I debugged a local failing session where:
commitIn this repro, the wrong assistant message still has the correct latest
parentID, so the session/message ordering appears correct. The stale jump seems to be caused by replayed model context, not by selecting the wrong session/message.Corrected hypothesis
My original hypothesis was too broad: it treated
reasoning.encrypted_contentin astore:falserequest as suspicious by itself.That is probably wrong.
OpenAI's reasoning docs say that in stateless mode (
store:falseor zero data retention), clients must still retain and resend reasoning items, and must requestreasoning.encrypted_contentviaincludeso those reasoning items can be replayed on future requests.So the bug is not simply "
encrypted_contentexists in the request". For current tool-call follow-ups and current-turn continuation,encrypted_contentis expected and needed.The likely bug is narrower:
In other words: keep hidden reasoning where OpenAI requires it, but do not replay stale hidden reasoning from older tasks into a new user turn.
Relevant code paths
The OpenAI request path in
packages/opencode/src/provider/provider.tssanitizes response item IDs before sending the final POST body:The safer layer for stale reasoning handling appears to be message normalization before the SDK converts model messages into Responses input: remove old assistant reasoning parts before the latest user message for
@ai-sdk/openai+store:false, but preserve latest-turn reasoning and valid tool-call continuation.Likely fix direction
A fix should probably:
store:falseThis matches OpenAI's stateless Responses guidance better than treating all encrypted reasoning as stale.
Steps to reproduce
I do not have a tiny deterministic repro yet, but the pattern is:
store:falseor equivalent zero-retention behaviorcommitExpected behavior
The assistant should continue the latest user request.
Actual behavior
The assistant sometimes replies to or continues an older task with no API error.
Additional context
I saw this on a local repro session where the stale turn resumed an earlier topic ("Clarifying code parameters") immediately after the user sent
commit.Related broader stale-context issues:
out of contextalongside thestore:falsereplay errors