Skip to content

feat(codex-encryp-cont-retry): add a feature in api error handling#2581

Closed
OsakaLOOP wants to merge 1 commit intofarion1231:mainfrom
OsakaLOOP:feat/codex-encryp-cont-retry
Closed

feat(codex-encryp-cont-retry): add a feature in api error handling#2581
OsakaLOOP wants to merge 1 commit intofarion1231:mainfrom
OsakaLOOP:feat/codex-encryp-cont-retry

Conversation

@OsakaLOOP
Copy link
Copy Markdown

@OsakaLOOP OsakaLOOP commented May 5, 2026

Codex encrypts unknown intermediate reasoning state into session-stored encrypted_content blocks, which break when the proxy rotates to a provider using a different API key, or the provider too cheap to serve users with different API keys changing with time — the new key cannot decrypt blobs produced by the old one, and the upstream returns invalid_encrypted_content. This feature detects that error from the forwarder's UpstreamError body, strips encrypted_content from both the include parameter and any reasoning or compaction items in the request input, then retries with the cleaned body. Once a provider triggers this error it is remembered for 30 minutes and subsequent requests are proactively stripped to avoid the round-trip entirely. The toggle is placed under the failover error-handling section rather than as a separate panel, since it is a recovery behavior rather than a standalone configuration concern.

Refs:

Summary / 概述

Codex stores encrypted reasoning blocks in its session state, keyed to the user's API key. When CC-Switch routes to a different provider — or when a routing service cycles API keys over time — the old blocks become unreadable, and the OpenAI upstream returns:

    {"error":{"message":"The encrypted content gAAA...SQ== could not be verified.","type":"invalid_request_error","code":"invalid_encrypted_content"}}

Since CC-Switch already sits in the error-handling path, I find it makes sense to catch this and recover automatically. This PR does exactly that: on detecting an invalid_encrypted_content error, the proxy strips encrypted_content from the include parameter and from any reasoning/compaction items in the request input, then retries silently. A provider that triggers the error is remembered for 30 minutes so that subsequent requests are proactively cleaned, avoiding the round-trip entirely. No CLI interruption, no session rewrite.

The toggle lives under the failover section in proxy settings, since this is an error-recovery behavior rather than a standalone feature. Enabled by default; turn it off only if all your providers share the same API key.

Discussions remain open for any missed edge cases, code issues, or localization corrections.

Related Issue / 关联 Issue

None

Fixes #

Screenshots / 截图

image

Item added in Settings.

Checklist / 检查清单

  • pnpm typecheck passes / 通过 TypeScript 类型检查
  • pnpm format:check passes / 通过代码格式检查
  • cargo clippy passes (if Rust code changed) / 通过 Clippy 检查(如修改了 Rust 代码)
  • Updated i18n files if user-facing text changed / 如修改了用户可见文本,已更新国际化文件

Codex encrypts unknown intermediate reasoning state into session-stored encrypted_content blocks, which break when the proxy rotates to a provider using a different API key, or the provider too cheap to serve users with different API keys changing with time — the new key cannot decrypt blobs produced by the old one, and the upstream returns invalid_encrypted_content. This feature detects that error from the forwarder's UpstreamError body, strips encrypted_content from both the include parameter and any reasoning or compaction items in the request input, then retries with the cleaned body. Once a provider triggers this error it is remembered for 30 minutes and subsequent requests are proactively stripped to avoid the round-trip entirely. The toggle is placed under the failover error-handling section rather than as a separate panel, since it is a recovery behavior rather than a standalone configuration concern.

Refs:
- Codex session handover & encrypted summary: https://tonylee.im/zh-CN/blog/codex-compaction-encrypted-summary-session-handover/
- Encrypted reasoning items: https://developers.openai.com/api/docs/guides/reasoning#encrypted-reasoning-items
- Community report of the same issue: https://linux.do/t/topic/2102361
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5273873c08

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +564 to +568
ctx.provider = retry_result.provider;
return process_response(
retry_result.response,
&retry_ctx,
&state,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Update retry context provider before processing response

On successful retry, the code assigns ctx.provider = retry_result.provider but then calls process_response with &retry_ctx, whose provider was never updated. In this handler, process_response uses ctx.provider for usage/accounting and provider attribution, so failover retries can be logged against the wrong provider whenever the retry lands on a different backend.

Useful? React with 👍 / 👎.

Comment on lines +578 to +583
let wrapped = ProxyError::Internal(format!(
"[CC Switch] Auto-detected invalid_encrypted_content, stripped encrypted_content from request (include + input items), and retried — but the retry also failed.\nOriginal error: {}\n\nTip: If you see this repeatedly, try restarting your Codex session with --resume to clear session state.",
retry_err.error
));
log_forward_error(&state, &ctx, is_stream, &wrapped);
return Err(wrapped);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve upstream retry error instead of forcing internal 500

If the strip-and-retry attempt fails, this path wraps the failure into ProxyError::Internal, which converts all retry failures into HTTP 500 and discards the original upstream status/body (for example, a meaningful 4xx validation error). Before this change, upstream failures propagated as UpstreamError, so clients could see the true status and payload.

Useful? React with 👍 / 👎.

@OsakaLOOP OsakaLOOP closed this May 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant