Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

[Bug] OpenCodex 2.88.0: existing threads fail after model/provider switching when encrypted reasoning is rejected inside HTTP 200 SSE

Open
#7,369 2 comments 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

@Ingwannu is already working on this.

Since Oct 19, 2026.

  • #7383 by @Ingwannu — open

Assessment

Difficulty
5/5
Estimated time
Over a week
Newbie friendliness
35/100
Issue type
Bug
Clarity
Mostly clear
Activity status
Active
Tech stack
typescript
Domain
api, backend

Research direction

Start with src/server/responses/core-opaque-recovery.ts and src/server/responses/passthrough-dispatch.ts, then trace the related stream preflight and reasoning serializer in src/server/responses/combo-stream-preflight.ts and src/adapters/openai-responses/reasoning.ts. Compare HTTP error and HTTP 200 SSE rejection handling, using the synthetic probe in the issue to reproduce the failure. Done means one bounded recovery handles explicit ciphertext rejections while preserving valid native history, tool associations, and on-disk conversation data; the report says no upstream-ready patch or PR is included.

Written by the indexing model from the issue text.

Description

bug proxy streaming tools
Client or integration

Codex App

Area

Proxy and routing

Summary

Also reproduced with a direct HTTP request to the local /v1/responses endpoint.

On OpenCodex 2.88.0, an existing conversation can stop working after switching models or providers. Its history still contains reasoning encrypted_content that the destination backend cannot verify, but some rejection shapes do not enter the existing one-shot recovery path.

The important failure mode is HTTP 200 with a failed SSE terminal and no assistant answer. Checking the HTTP status alone incorrectly suggests success. Further client retries can replay the same rejected history.

The expected behavior is to continue the original conversation while preserving its complete on-disk history. Recovery should operate on an outbound request copy, require an explicit upstream ciphertext rejection, and remain limited to one sanitized rebuild before any output or tool call has committed. Valid native ciphertext and normal same-model continuations must remain supported.

Reproduction

Observed workflow

  1. Configure native OpenAI/ChatGPT Responses and DeepSeek Responses routes.
  2. Continue a conversation long enough to accumulate reasoning history. Tool calls and results may also be present.
  3. Switch the model/provider within that conversation and continue. Restoring an existing conversation after restarting OpenCodex is another relevant case because the in-memory route record may be unavailable.
  4. If the destination rejects the replayed ciphertext, inspect the SSE terminal and actual answer, not only the HTTP status.

Minimal synthetic probe

Save the following as issue-repro.json. The deliberately invalid value exercises rejection recovery; it is not real ciphertext or private conversation data and does not imply that arbitrary encrypted history should be removed.

{
  "model": "gpt-6.1-sol",
  "stream": true,
  "store": false,
  "instructions": "Answer the final user question concisely. Do not call tools.",
  "reasoning": { "effort": "low" },
  "tools": [],
  "input": [
    {
      "type": "message",
      "role": "user",
      "content": [{ "type": "input_text", "text": "For this compatibility test remember code MAPLE-73. Reply only with that code." }]
    },
    {
      "type": "reasoning",
      "summary": [],
      "encrypted_content": "c1f30500-0000-0000-0000-000000000000-0"
    },
    {
      "type": "message",
      "role": "assistant",
      "content": [{ "type": "output_text", "text": "MAPLE-73" }]
    },
    {
      "type": "message",
      "role": "user",
      "content": [{ "type": "input_text", "text": "What code did I ask you to remember? Reply only with the code." }]
    }
  ]
}

On Windows:

curl.exe --no-buffer http://127.0.0.1:10100/v1/responses `
  -H "Content-Type: application/json" `
  -H "Accept: text/event-stream" `
  --data-binary "@issue-repro.json"

This requires an available native OpenAI/ChatGPT route. Supply the instance's authentication header separately if local data-plane authentication is enabled.

Before the local patch: HTTP 200, failed stream terminal, no remembered code returned.

With the local prototype: completed terminal, correct MAPLE-73 answer, and no client-visible ciphertext error. The usage record showed two sends, with the second attributed to opaque-blob-rejection recovery.

Version

@bitkyc08/opencodex 2.88.0 (global npm; bundled Bun 1.4.2). Earlier/later releases not systematically tested; no fixed upstream release claimed.

Operating system

Windows 11, 64-bit, OS version 10.0.26200.

Provider and model

OpenAI/ChatGPT native Responses: gpt-6.1-sol; DeepSeek Responses: deepseek/deepseek-flash. Tested OpenAI → DeepSeek → OpenAI, including tool-call history.

Logs or error output
The encrypted content <redacted> could not be verified. Reason: Encrypted content could not be decrypted or parsed.

Error type: invalid_request_error
Error code: invalid_encrypted_content

The rejection can also use the exact verification message without a dedicated code, or arrive as an `error` / `response.failed` event inside HTTP 200 SSE. An intermediate projection may surface it as `502 upstream_server_error`. Ordinary network failures, rate limits, and quota errors should not qualify for ciphertext recovery.
Screenshots and supporting files
Root-cause analysis

In the examined 2.88.0 code:

  • src/server/responses/core-opaque-recovery.ts: the 502 exception in shouldAttemptOpaqueBlobRecovery() requires encrypted function output and its specific decrypt/decode rejection. A reasoning-only ciphertext rejection does not satisfy that exception.
  • src/server/responses/passthrough-dispatch.ts: stream preflight is gated mainly on encrypted function output or _nativeReasoningRetainedAcrossRoute. Restored history can still carry rejected reasoning state without that retention marker, particularly when route provenance is missing after a restart.
  • The recovery boundary also needs to avoid mutating caller-owned history. Proxy-owned ocxr1: envelopes must be distinguished from native ciphertext: their readable content can be decoded, whereas native ciphertext cannot be reconstructed by the proxy.
Proposed solution
  1. Recognize explicit rejection identities consistently. Share the classifier across HTTP errors and supported SSE error envelopes, including nested and flat forms. Admit a 502 only when the response explicitly identifies a ciphertext rejection and the actual outbound body still contains opaque state. Generic gateway errors must remain excluded.
  2. Cover restored/cold history. On Responses-compatible routes, inspect the bounded, zero-output stream prelude when the outbound request contains opaque history, even if no retention marker or route-cache entry exists. Preserve the stream unchanged if it succeeds or commits output.
  3. Rebuild an outbound copy once. Remove rejected, nonportable reasoning/compaction state through the existing serializer and drop foreign reasoning item IDs where required. Preserve message order, tool arguments, results, and call_id associations. Do not rewrite the on-disk conversation. If a body is cloned, carry over its existing privacy/non-persistence policy.
  4. Preserve decodable content. Decode recognized proxy-owned envelopes and retain their real text. In local testing, unsigned reasoning summaries alone were not sufficient for the destination model to use that text; projecting it into a clearly labelled historical message worked. Never fabricate plaintext for native ciphertext or describe it as decrypted.
  5. Keep retry boundaries strict. Respect the shared send budget and one-shot guard. Do not replay after text, a tool-call event, or other committed output. A repeated rejection remains terminal. Keep valid native ciphertext on unaffected continuations, and retain the original history for switching back.
Temporary local workaround and deployment

A version-specific outbound compatibility patch was applied to the installed 2.88.0 package and the actual Bun service was restarted. The four affected modules were:

src/server/responses/core-opaque-recovery.ts
src/server/responses/passthrough-dispatch.ts
src/server/responses/combo-stream-preflight.ts
src/adapters/openai-responses/reasoning.ts

For an npm installation, the patch must reach the source actually loaded by the service. Verify that the running instance has reloaded it, then check a completed SSE terminal and the expected answer; a successful restart message or health endpoint alone is insufficient. npm upgrades/reinstalls can overwrite an ad-hoc source patch.

No configuration-only workaround has been verified. Creating a new conversation, deleting history, or clearing context is not the proposed solution.

Local validation and limits
  • The installed-version prototype passed 39 targeted tests and 11 online cases covering rejection recovery, cross-provider round trips, tool history, valid native reasoning replay, and readable proxy reasoning/compaction envelopes.
  • Valid native same-model replay required one send, without ciphertext recovery.
  • The complete file hashes of two affected conversations remained unchanged.
  • The original conversation was not retried in full in the Codex UI. Only a read-only excerpt of its real history was replayed online; this does not establish full recovery of the original conversation.
  • Other providers and operating systems remain unverified. These passing counts belong to the local 2.88.0 prototype, not the full upstream suite. A separate broader development run still had unresolved failures; no upstream-ready patch or PR is included with this report.

Related closed reports:

This report focuses on the remaining 502 / HTTP 200 SSE recovery gap when reasoning history is present but the retention marker or route record is unavailable.

Redacted configuration
{
  "providers": {
    "openai": {
      "adapter": "openai-responses",
      "baseUrl": "https://chatgpt.com/backend-api/codex"
    },
    "deepseek": {
      "adapter": "openai-responses",
      "baseUrl": "https://api.deepseek.com"
    }
  }
}
Checks
  • I searched existing issues and documentation.
  • I removed secrets, tokens, account details, request credentials, and personal data.
Dominant language
TypeScript
Stars
16.9k
Forks
1.3k
Avg merge
7h 34m
Merged PRs (30d)
667

Getting set up

This project ships no dev container, Dockerfile or contributing guide, so setting up is up to you: start from its README, and see our first-contribution guide for the general steps.

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.

More from lidge-jun/opencodex

All issues in lidge-jun/opencodex

Similar issues

More TypeScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.