fix: recover from previous_response_not_found instead of passing it through#418
Merged
icebear0828 merged 1 commit intodevfrom Apr 27, 2026
Merged
Conversation
…hrough When the upstream rejects a request with `previous_response_not_found` (response was created by a different account, the in-memory affinity map was lost on restart / 4h TTL expiry, or rotation forced the request onto a different account), the proxy now strips the stale `previous_response_id` + `turnState`, retries once on the same account, and forgets the stale entry from `SessionAffinityMap` so subsequent requests aren't routed to the same wrong account. Two halves are required because the error reaches the proxy as an in-stream WS frame, not as a synchronous HTTP error: 1. `ws-transport.ts:36` — add `previous_response_not_found: 400` to `ROTATABLE_ERROR_CODES` so the first-frame error is converted to a `CodexApiError` reject, letting the proxy-handler catch block run. 2. `proxy-handler.ts` — in the catch, detect via the new `isPreviousResponseNotFoundError` classifier, reuse the existing `restoreImplicitResumeRequest()` path (so implicit-resume requests replay the full input losslessly), then clear the stale ID + turn state and retry. A `prevRespNotFoundRetried` flag prevents looping if the second attempt also fails. Implicit-resume scenarios recover losslessly. Explicit-resume requests (client-supplied `previous_response_id`) lose server-side history but still complete with the latest input — better than a hard error that most clients can't gracefully handle. Verified end-to-end against real chatgpt.com upstream: 3/3 successful recoveries with `affinity=miss` + `previous_response_not_found` → `stripping and retrying same account` → 200 OK.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
previous_response_not_found(response 由别的账号创建 /SessionAffinityMap过期或重启丢失 / 跨账号轮转)时,proxy 自动剥掉previous_response_id+turnState,在同一账号上重试一次,并把这条 ID 从 affinity map 清掉CodexApiErrorreject(ws-transport.tsROTATABLE_ERROR_CODES增补),再在 catch 里 strip + retry(proxy-handler.ts)restoreImplicitResumeRequest()路径回放完整 input,无损恢复;显式续链丢服务端历史但请求仍能完成Why
之前这个错误从 chatgpt.com 以 in-stream WS 错误帧到达,不在
ROTATABLE_ERROR_CODES白名单里 → 直接被流式翻译器格式化成[Error] previous_response_not_found:透传给客户端。proxy-handler 的 catch 块根本没触发。客户端(Codex CLI / Claude Code 等)没法 graceful 处理这个错误,整个对话上下文丢失就挂了。Changes
src/proxy/error-classification.ts— 新增isPreviousResponseNotFoundError,按 body code + message 兜底两路识别src/proxy/ws-transport.ts—ROTATABLE_ERROR_CODES增补previous_response_not_found: 400src/auth/session-affinity.ts— 新增forget(responseId)方法src/routes/shared/proxy-handler.ts— catch 块新增 strip-and-retry,复用restoreImplicitResumeRequest(),loop guard 防死循环tests/unit/proxy/ws-transport-early-error.test.ts— WS 首帧previous_response_not_found→CodexApiError(400)tests/integration/proxy-handler.test.ts— strip-retry 恢复路径 + loop guardTest plan
npm test— 1619 tests pass (143 files)npx tsc --noEmit— 干净affinity=miss→previous_response_not_found→stripping and retrying same account→ 200 OK/v1/responses+ 已知 staleprevious_response_id触发