Skip to content

openai-codex provider: multi-turn sessions permanently break with 400 invalid_encrypted_content after an OAuth token refresh #84511

Description

@TaeseongMoon

openai-codex provider: multi-turn sessions permanently break with 400 invalid_encrypted_content after an OAuth token refresh

Summary

When the agent model is openai-codex/gpt-5.5 authenticated via a ChatGPT-plan OAuth profile, any multi-turn session works for a while and then every subsequent turn fails silently with HTTP 400 invalid_encrypted_content from the Codex Responses API.

The failure is deterministically triggered by an OAuth access-token refresh. OpenClaw replays encrypted_content reasoning items from earlier turns; those blobs are bound to the access token that produced them, so once the token is refreshed (periodic refresh, or re-auth on gateway restart) the Codex backend can no longer decrypt them and rejects the whole request.

The user receives no reply at all — the run ends as an empty non_deliverable_terminal_turn.

Environment

  • OpenClaw 2026.5.18 (build 50a2481, trace gitSha 39ee0dd)
  • macOS 26.2 (arm64), Node v25.9.0
  • Provider openai-codex, model gpt-5.5, api openai-codex-responses, harness pi
  • Auth: ChatGPT-plan OAuth (auth_mode=chatgpt, plan pro)
  • Gateway mode local

Steps to reproduce

  1. Configure an agent with model.primary = openai-codex/gpt-5.5 using a ChatGPT-plan OAuth auth profile.
  2. Start a fresh session and send several turns that involve tool calls (so reasoning items accumulate in history). They succeed.
  3. Wait for the openai-codex OAuth access token to be refreshed — happens periodically, and on every gateway restart (resolving authentication…). The refresh rewrites ~/.openclaw/agents/<id>/agent/auth-profiles.json + auth-state.json.
  4. Send another turn in the same session.

Expected

The turn completes normally. A token refresh should not invalidate an in-flight conversation.

Actual

Every turn after the refresh fails:

[openai-transport] WARN  [responses] error provider=openai-codex api=openai-codex-responses
  model=gpt-5.5 name=Error status=400 code=invalid_encrypted_content type=invalid_request_error
  message=400 The encrypted content gAAA...= could not be verified.
  Reason: Encrypted content could not be decrypted or parsed.
[agent/embedded] WARN  embedded run agent end
[agent/embedded] WARN  embedded run failover decision
[diagnostic] ERROR lane task error: lane=main durationMs=2505
  error="FailoverError: LLM request failed: provider rejected the request schema or tool payload."
[model-fallback/decision] WARN model fallback decision
ERROR Embedded agent failed before reply: LLM request failed: provider rejected the request schema or tool payload.

Run trajectory for the failed run:

{ "type": "model.completed",
  "data": { "terminalError": "non_deliverable_terminal_turn",
            "assistantTexts": [],
            "promptCache": { "lastCallUsage": { "input": 0, "output": 0, "total": 0 } } } }
{ "type": "session.ended", "data": { "status": "error",
            "terminalError": "non_deliverable_terminal_turn" } }

The session stores "[assistant turn failed before producing content]" (or an empty assistant message) and the user gets nothing back.

Evidence — refresh correlates exactly with the breakage

Session e4a909f8:

Time (KST) Event Result
17:05 / 17:10 / 17:12 / 17:15 4 turns success
17:19:25 auth-profiles.json + auth-state.json rewritten (token refresh)
17:20:23 next turn 400 invalid_encrypted_content

Session bf79fe90:

Time (KST) Event Result
16:09 first turn success
16:29:56 gateway restart → resolving authentication… (token re-issued)
16:33 / 16:38 / 17:03 / 17:04 every turn 400 invalid_encrypted_content

Trivial sessions (e.g. heartbeat) keep working because they don't replay accumulated reasoning items.

Root cause (two distinct issues)

1. Stale encrypted_content reasoning replay.
For openai-codex-responses, OpenClaw replays encrypted_content reasoning items from prior turns. These blobs are cryptographically bound to the OAuth access token that generated them. After a token refresh, all previously-stored encrypted_content is undecryptable, but OpenClaw keeps sending it.

Note: requiresReasoningContentReplay("gpt-5.5") returns false (gpt-5.5 is not in REASONING_CONTENT_REPLAY_MODEL_IDS = kimi-* / mimo-* / deepseek-v4-*), so the transcript policy resolves dropReasoningFromHistory: true. Despite that, the outgoing request still contains stale encrypted_content and is rejected — so either the codex provider path bypasses the dropReasoningFromHistory transcript policy, or dropReasoningFromHistory / shouldPreserveCurrentToolTurnReasoning does not strip encrypted_content thoroughly enough. Maintainers should verify which.

2. No recovery on invalid_encrypted_content.
The 400 code=invalid_encrypted_content is classified as FailoverError: provider rejected the request schema or tool payload and routed to model-fallback. With no fallback model configured, the run hard-fails with zero output. invalid_encrypted_content is a recoverable, well-known Codex error and should not be treated as a schema/tool-payload rejection.

Suggested fix

  • On 400 code=invalid_encrypted_content: retry once on the same model after stripping all encrypted_content from reasoning items in the request. Do not route to model fallback.
  • And/or: tag stored reasoning items with the credential/token id that produced them; when the active openai-codex token changes, drop reasoning items whose token id no longer matches before building the request.
  • And/or: drop/invalidate stored encrypted_content for affected sessions whenever the openai-codex OAuth token is refreshed.

Impact

Any multi-turn openai-codex (ChatGPT-plan OAuth) session becomes permanently unusable after the first token refresh — which happens roughly hourly and on every gateway restart. The user gets a completely silent failure (empty turn, no error surfaced to the channel).

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1High-priority user-facing bug, regression, or broken workflow.impact:auth-providerAuth, provider routing, model choice, or SecretRef resolution may break.impact:message-lossChannel message delivery can be lost, duplicated, or misrouted.impact:session-stateSession, memory, transcript, context, or agent state can drift or corrupt.

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions