Skip to content

[Bug] OpenAI Responses store:false can replay stale reasoning from older turns #21020

Closed as not planned

Description

@Cleroth

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:

  1. the user sends commit
  2. the assistant correctly starts commit-related work
  3. 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:

  1. Use stock OpenAI GPT-5 on the Responses path
  2. Ensure requests are using store:false or equivalent zero-retention behavior
  3. Let the assistant perform at least one tool-using task
  4. Send a short follow-up that changes task, for example commit
  5. 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:

Activity

  1. added
    coreAnything pertaining to core functionality of the application (opencode server stuff)
    on Apr 4, 2026
  2. github-actions commented on Apr 4, 2026

    @github-actions
    Contributor

    This issue might be a duplicate of existing issues. Please check:

  3. Cleroth commented on Apr 4, 2026

    @Cleroth
    Author

    Probably related: #16612 and #19548

  4. changed the title [-][Bug] OpenAI Responses with store:false can silently resume an older task instead of the latest user turn[/-] [+][Bug] OpenAI Responses store:false can replay stale reasoning from older turns[/+] on Apr 25, 2026
  5. Svtter commented on Apr 25, 2026

    @Svtter

    Your hypothesis about stale reasoning in store:false mode is correct. I identified the specific code paths — see full analysis in #24190: #24190 (comment)

    The two most relevant root causes for your OpenAI case:

    1. transform.ts:508-516 — include: ["reasoning.encrypted_content"] only set in variant options, so default mode never requests encrypted reasoning content
    2. convert-to-openai-responses-input.ts:208-237 — The store:false branch doesn't properly handle reasoning items with empty summary text but valid encrypted_content

    Vercel AI SDK fixed similar issues in vercel/ai#12869 and vercel/ai#14418, but opencode's custom converter has the same bugs.

  6. rekram1-node commented on Apr 30, 2026

    @rekram1-node
    Contributor

    This is not a bug, we match the implementation of codex.

  7. Cleroth commented on Jun 1, 2026

    @Cleroth
    Author

    This is not a bug, we match the implementation of codex.

    It does actually seem to be a bug in codex. After implementing the above fix, I haven't experienced this ever again in the two months that followed, despite having encountered it fairly often before it.

    Many people report codex/gpt occasionally replying to a previous message. They chalk it up to the model being stupid but it seems to be bug in harnesses instead.

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

Metadata

Metadata

Assignees

Labels

coreAnything pertaining to core functionality of the application (opencode server stuff)

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions