feat(codex-encryp-cont-retry): add a feature in api error handling#2581
feat(codex-encryp-cont-retry): add a feature in api error handling#2581OsakaLOOP wants to merge 1 commit intofarion1231:mainfrom
Conversation
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
There was a problem hiding this comment.
💡 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".
| ctx.provider = retry_result.provider; | ||
| return process_response( | ||
| retry_result.response, | ||
| &retry_ctx, | ||
| &state, |
There was a problem hiding this comment.
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 👍 / 👎.
| 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); |
There was a problem hiding this comment.
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 👍 / 👎.
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 / 截图
Item added in Settings.
Checklist / 检查清单
pnpm typecheckpasses / 通过 TypeScript 类型检查pnpm format:checkpasses / 通过代码格式检查cargo clippypasses (if Rust code changed) / 通过 Clippy 检查(如修改了 Rust 代码)