Repository navigation
fix(openai): move to the next account when the stripped retry fails for the account's own reason - #249
Merged
Conversation
…or the account's own reason Reasoning recovery strips unverifiable ciphertext and re-sends once on the same account. When that re-send failed for a reason that belongs to the account (credit exhausted, rate limit, auth, 5xx, an upstream failure inside the stream), the attempt ended as a recovery terminal and the client got the failure, although any other account could have answered the unchanged request. Seen in production on 2026-10-05: the stripped retry on one account ended twice with credit_balance_exhausted and the request stopped there. Such a failure is now returned as an ordinary account failover carrying a mark (RecoveryRetrySpent): - Only on POST /v1/responses (native, compact, passthrough), only when the retry was dispatched, nothing reached the client, and the failure is one a first send would move to another account. Validation rejections, refusal outcomes, transport errors, header timeouts and unclassified errors still end on the account. - The handler does not retry the same account (its one stripped retry is spent), applies the retry-exhausted cooldown, and resets the recovery attempt so the next account gets the original request and its own retry. - HTTP answers keep the classification of a first send: a request-budget rejection stays request-scoped, model_not_supported stays scoped to the account/model pair, a budget_exceeded 429 stays the account's, and other pool-retryable statuses stay outside the API-key health breaker. No forward-layer account state is written on this exit. - Ops continuation diagnostics mark the attempt with recovery.account_failure. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comments and handbook wording only; no behaviour change. - The handler's retry-exhausted cooldown does nothing for an OpenAI API-key account, so the comment, the test comment and the handbook no longer say such an account is cooled after an HTTP answer to its stripped retry. - A non-failover answer to the stripped retry ends the request unless it is a ciphertext rejection, which StopError still hands on as a mismatch. - A recovery terminal after committed output needs a ciphertext rejection. - The handbook names per path which answers move to the next account: the passthrough rule is narrower than the native one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
What
Reasoning recovery strips unverifiable ciphertext and re-sends once on the same account. When that re-send failed for a reason that belongs to the account (credit exhausted, rate limit, auth, 5xx, an upstream failure inside the stream), the attempt ended as a recovery terminal and the client received the failure, although another account could have answered the unchanged request.
Observed in production after
v0.2.13-codexrip.9: the stripped retry on one account ended twice withcredit_balance_exhaustedand the request stopped there.Change
Such a failure is now returned as an ordinary account failover carrying a mark (
UpstreamFailoverError.RecoveryRetrySpent).POST /v1/responsesonly (native, compact, passthrough). Chat Completions → Responses is unchanged.model_not_supportedstays scoped to the account/model pair, abudget_exceeded429 stays the account's, other pool-retryable statuses stay outside the API-key health breaker. This exit writes no forward-layer account state.recovery.account_failure.The recovery policy (
codexruntime/recovery),nativeapiand the decision order are untouched.Tests
model_not_supported/budget_exceeded).TestOllamaProbeCallback_StaleLongDoesNotOverrideNewShortfails on this Windows machine onmainas well; it is unrelated to this change.🤖 Generated with Claude Code