Skip to content

fix(anthropic): fail over on proven pre-output account 403 refusals - #6347

Merged
lidge-jun merged 1 commit into
devfrom
codex/fix-6340-anthropic-pool-403
Oct 1, 2026
Merged

lidge-jun merged 1 commit into
devfrom
codex/fix-6340-anthropic-pool-403

Conversation

@lidge-jun

Copy link
Copy Markdown
Owner

Summary

The Anthropic OAuth account pool rotated to the next account only on HTTP 429. When one pooled account's Claude subscription had expired, the upstream answered 403 before any output, and the request failed even though other accounts in the pool were healthy.

A pre-output 403 now fails over to the next account only when it proves an account-level refusal. That requires complete, bounded JSON, a recognized error type, and exact account-entitlement or billing wording, and the bearer and credential generation that actually sent the request must still match. A recognized refusal clears affinity and cools that account, using Retry-After or ten minutes by default, then the request moves to the next account under the existing rotation cap and send budget. Generic permission, policy, malformed, overridden-credential and stale-credential 403s stay terminal. No 403 failover happens after output has begun. Main dispatch, continuations, sidecars and web search follow the same rule. Docs and translations that described 429-only rotation are updated.

Closes #6340

Verification

  • bun test tests/adapters/anthropic/anthropic-quota-dispatch.test.ts: red 18 pass / 9 fail before the fix, green 40 pass / 0 fail after. It covers entitlement recovery, negative classifications, credential replacement, header overrides, exhausted pools, strict routes, continuations and sidecars.
  • Rebased on current dev: the changed tests plus test-layout and file-size guards pass (67 pass / 0 fail); bun x tsc --noEmit, structure:check and privacy:scan pass.
  • Security review (account-pool credential surface): PASS by an independent reviewer. The classifier is bounded and fails closed on unknown wording. Attribution binds only when the outgoing headers prove the selected bearer. New diagnostics carry no tokens, account identifiers, emails or upstream bodies. There is no new credential destination.
  • Not tested against a live expired Claude account. Cooldowns are process-local, as for 429.
  • Cross-platform CI for this head is deferred by the owner's instruction to one run on the final dev tip before release.

Checklist

  • Scope stays focused and avoids unrelated cleanup.
  • Docs or release notes were updated when needed.
  • Security-sensitive changes were reviewed for secrets, auth, and unsafe defaults.

@lidge-jun
lidge-jun requested a review from Ingwannu as a code owner October 1, 2026 02:34
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-01T02:39:05.044077Z b948392 PR opened
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@github-actions

github-actions Bot commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

✅ Deterministic PR hygiene checks passed.

@github-actions github-actions Bot added the bug Something isn't working label Oct 1, 2026
@coderabbitai

coderabbitai Bot commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

⚙️ Run configuration

Configuration used: Repository: lidge-jun/opencodex/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Advanced

Run ID: 86325c9e-9481-4431-b783-04651fd999c4

📥 Commits

Reviewing files that changed from the base of the PR and between 09a86e1 and b948392.

📒 Files selected for processing (26)
  • docs-site/src/content/docs/fr/guides/claude-code.md
  • docs-site/src/content/docs/fr/reference/configuration/providers.md
  • docs-site/src/content/docs/guides/claude-code.md
  • docs-site/src/content/docs/ja/reference/configuration/providers.md
  • docs-site/src/content/docs/ko/reference/configuration/providers.md
  • docs-site/src/content/docs/reference/configuration/providers.md
  • docs-site/src/content/docs/ru/reference/configuration/providers.md
  • docs-site/src/content/docs/tr/guides/claude-code.md
  • docs-site/src/content/docs/tr/reference/configuration/providers.md
  • docs-site/src/content/docs/zh-cn/reference/configuration/providers.md
  • docs-site/src/content/docs/zh-tw/guides/claude-code.md
  • docs-site/src/content/docs/zh-tw/reference/configuration/providers.md
  • src/images/loop.ts
  • src/oauth/anthropic-account-refusal.ts
  • src/oauth/anthropic-routing.ts
  • src/server/responses/adapter-continuation.ts
  • src/server/responses/adapter-delivery.ts
  • src/server/responses/adapter-dispatch.ts
  • src/server/responses/request-transport.ts
  • src/server/responses/sidecar-execution.ts
  • src/web-search/loop.ts
  • structure/providers-and-adapters.md
  • structure/providers/anthropic-account-pool.md
  • structure/transports/inventory.md
  • structure/transports/responses-failover.md
  • tests/adapters/anthropic/anthropic-quota-dispatch.test.ts
 _______________________________________________________________________________
< FUZZY BUNNY: Finding Unresolved Zero-day Zingers, Bugs, Nasties, and Yuckies. >
 -------------------------------------------------------------------------------
  \
   \   (\__/)
       (•ㅅ•)
       /   づ
✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Autopilot is currently an internal CodeRabbit preview.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@lidge-jun

Copy link
Copy Markdown
Owner Author

Maintainer integration into dev by @lidge-jun. Security review (account-pool credential surface): PASS by an independent reviewer; details and red-green evidence are in the description. Rebased on current dev; changed tests and guards 67 pass, typecheck, structure and privacy checks pass. Cross-platform CI for this head is deferred by the owner's instruction to one run on the final dev tip before release; scripts/ci/assert-mergeable-review.sh --maintainer-integration OK at this head.

@lidge-jun
lidge-jun merged commit c4a5219 into dev Oct 1, 2026
14 of 28 checks passed
@lidge-jun
lidge-jun deleted the codex/fix-6340-anthropic-pool-403 branch October 1, 2026 02:35

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

Copy link
Copy Markdown

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: b94839264e

ℹ️ 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".

allowAccountRefusal?: boolean;
},
): Promise<string | null> {
if (options.signal?.aborted || isNonReplayableResponse(response)) return null;

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 429 cooldown recording after cancellation

When an upstream 429 has already resolved but the client aborts before this helper runs, this early return skips both rotation and recordAnthropicAccountRefusal. Previously the 429 path recorded the sender's cooldown even when no retry could be made, so the next request now may immediately select an account already proven rate-limited. Keep cancellation from authorizing a resend, but still record a replayable 429—for example, by applying the abort guard only to 403 body classification and passing canRetry: false for an aborted 429.

AGENTS.md reference: src/AGENTS.md:L17-L19

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant