Skip to content

fix(anthropic): record account 403 rotations as oauth-account-403, not a 429 - #7185

Closed
vadymhimself wants to merge 1 commit into
lidge-jun:devfrom
vadymhimself:fix/anthropic-credential-recovery-kind
Closed

vadymhimself wants to merge 1 commit into
lidge-jun:devfrom
vadymhimself:fix/anthropic-credential-recovery-kind

Conversation

@vadymhimself

@vadymhimself vadymhimself commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • An Anthropic OAuth rotation triggered by a classified account-entitlement 403 was recorded with recovery kind anthropic-oauth-429 on four routed paths: main adapter dispatch (adapter-dispatch.ts), the pre-output empty-completion continuation retry (adapter-continuation.ts), and the web-search / image sidecars (sidecar-execution.ts, which the loops' on429 hook passes through unchanged).
  • causeForRecoveryKind("anthropic-oauth-429") is rate-limit, so opencodex_recoveries_total, opencodex_request_failures_total and request-log failure attribution counted a credential/entitlement denial as a rate limit. An operator could not tell an account whose quota was spent (429) from one whose subscription/org was refused (403).
  • These sites now record the existing oauth-account-403 kind (cause credential-rejected) for a 403. The native Messages path (messages-native.ts) already does this. 401 stays oauth-401 and 429 stays anthropic-oauth-429.
  • No new roster member, cause, GUI label or locale string. Rotation, cooldown and send-budget behaviour are unchanged: the kind is only recorded (noteRoutedAttemptSend), and oauth-account-403 is already in the analytics cooldown-triggering set alongside anthropic-oauth-429.
  • structure/providers/anthropic-account-pool.md states the recorded kind.

Verification

  • Red on dev (0b13396), before the fix: the new tests/adapters/anthropic/anthropic-quota-dispatch.test.ts case %s account 403 rotation records oauth-account-403 failed for main, search, image and continuation, with recorded kinds ["anthropic-oauth-429", "anthropic-oauth-429"] (continuation also had empty-completion).
  • Green after the fix: bun scripts/test.ts tests/adapters/anthropic/ tests/web-search/web-search-recovery-kind.test.ts tests/images/loop.test.ts tests/lib/failure-stage-model.test.ts tests/usage/request-outcome-agreement.test.ts tests/claude-integration/messages-native-oauth.test.ts tests/oauth/generic-oauth-failover.test.ts: 1512 pass, 0 fail.
  • bun run typecheck: passes.
  • bun run structure:check: passes.
  • bun run privacy:scan: passes.
  • bun run test:changed (against upstream/dev): 14563 pass, 1 fail across 575 files. The one failure, tests/cli/cli-connect-readiness.test.ts › connected-client runtime probe scope > a rejected preferred runtime falls back without rewriting the saved selection, also fails on a clean dev worktree (2 of 2 runs there) and is unrelated to this change.
  • I did not run the full bun run test locally. test:changed covers the import graph of the three changed source files, and CI covers the rest.
  • No GUI changes.

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.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Improved recovery when an Anthropic account refuses a request with an access error. The affected request paths now retry using a rotated account, including standard requests, web searches, image requests, and continuations. This helps requests recover consistently instead of being treated as quota-related failures.

Review readiness checklist

This PR stays in draft until every box below is ticked. Tick all four boxes once the requirements are met:

  • Required local validation passed; commands, results, and any full-suite exception are documented.

  • I pushed my PR to a recent dev commit (at most 10 behind; a maintainer may still ask for the exact tip before merge).

  • I resolved all correct Codex and CodeRabbit findings.

  • My PR is ready for review.

@vadymhimself

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository: lidge-jun/opencodex/.coderabbit.yaml
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: 2e30ec29-7246-4570-bca8-6015cb86c740
📥 Commits

Reviewing files that changed from the base of the PR and between 0b13396 and c196b1b.

📒 Files selected for processing: 5
  • src/server/responses/adapter-continuation.ts
  • src/server/responses/adapter-dispatch.ts
  • src/server/responses/sidecar-execution.ts
  • structure/providers/anthropic-account-pool.md
  • tests/adapters/anthropic/anthropic-quota-dispatch.test.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 3 remain after this review.

📝 Walkthrough

Anthropic OAuth recovery paths now classify upstream 403 account refusals as oauth-account-403, while preserving 401 and other-status classifications. Tests cover rotated recovery across the main, search, image, and continuation paths. Documentation describes the 403 classification.

Changes: Anthropic 403 recovery

Layer / File(s) Summary
Classify recovery responses
src/server/responses/adapter-continuation.ts, src/server/responses/adapter-dispatch.ts, src/server/responses/sidecar-execution.ts
The recovery paths classify HTTP 403 responses as oauth-account-403. The dispatch and sidecar paths retain oauth-401 for HTTP 401 and anthropic-oauth-429 for other handled responses.
Verify and document 403 recovery
tests/adapters/anthropic/anthropic-quota-dispatch.test.ts, structure/providers/anthropic-account-pool.md
A parameterized test checks rotated recovery across the main, search, image, and continuation paths. The documentation describes the oauth-account-403 classification and credential-rejected cause.

Priority: ⬇️ Low

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix

Suggested reviewers: lidge-jun

Merge Risk: ⚪ Minimal · up to c196b

Account-refusal 403s are now recorded as credential rejections rather than rate limits, with 401/429 classifications and rotation behavior preserved. No actionable merge risk was identified.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 6 functions across 4 files. (1 skipped: 1… Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title clearly and concisely describes the main change: recording Anthropic account-entitlement 403 rotations as oauth-account-403 instead of anthropic-oauth-429.
Full details: Docstring Coverage

Explanation

Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 6 functions across 4 files. (1 skipped: 1 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

@coderabbitai

coderabbitai Bot commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@github-actions

Copy link
Copy Markdown
Contributor

✅ Deterministic PR hygiene checks passed.

@github-actions github-actions Bot added the bug Something isn't working label Oct 15, 2026
@vadymhimself
vadymhimself force-pushed the fix/anthropic-credential-recovery-kind branch from c196b1b to 4546764 Compare October 15, 2026 20:34
@github-actions

github-actions Bot commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

⏳ DRAFT

  • review readiness checklist open (0/4 boxes ticked).

What to do

  • Tick all four boxes in the PR description once you're done (currently 0/4).

Review readiness checklist

  • ⬜ Required local validation passed; commands, results, and any full-suite exception are documented.
  • ⬜ I pushed my PR to a recent dev commit (at most 10 behind; a maintainer may still ask for the exact tip before merge).
  • ⬜ I resolved all correct Codex and CodeRabbit findings.
  • ⬜ My PR is ready for review.

0/4 boxes ticked.

This PR stays in draft until every box above is ticked.

An Anthropic OAuth rotation triggered by a classified account-entitlement
403 was recorded as `anthropic-oauth-429` on the main dispatch, the
pre-output continuation retry and the web-search/image sidecars. Metrics
and failure attribution therefore counted credential/entitlement denials
as rate limits (cause `rate-limit`), so a spent quota and a refused
account were indistinguishable.

Record the existing `oauth-account-403` kind (cause `credential-rejected`)
for a 403, as the native Messages path already does. 401 stays
`oauth-401`, 429 stays `anthropic-oauth-429`; rotation behaviour is
unchanged.

Co-Authored-By: Claude Code <noreply@anthropic.com>
@lidge-jun

Copy link
Copy Markdown
Owner

Being carried into #7224 with your authorship preserved; this PR will be closed when the bundle lands.

lidge-jun added a commit that referenced this pull request Oct 16, 2026
…evidence fixes (#7191, #7204, #7187, #7209, #7175, #7185, #7193, #7197, #7198) (#7224)

Carried from #7191, #7204, #7187, #7209, #7175, #7185, #7193, #7197, #7198 (one commit per source PR, original author preserved). Security review follow-up: the Anthropic 402 combo cooldown now validates the sending credential's ownership before cooling an account.

Remote validation (hosted GitHub Actions unreliable; local tests not run by maintainer rule): union candidate codex/prr-integ-8 = dev c03eafd + #7224 + #7223 + #7225 + #7217 + #6454 + #6594 plus a Gentle Shell import-cycle fix (integ-9 e1f289c: typecheck + full suite on mini (Windows) and lidge (Linux), compared with the same host's dev runs) and a test-only Windows path-escaping fix for the reasoning-retention routing test (integ-10 0d4cba7, tree a2b309a: that file re-run on both hosts).

Co-authored-by: Vadym O <bolein95@gmail.com>
@lidge-jun

Copy link
Copy Markdown
Owner

Landed on dev in 6ccb47a via #7224 (your change is its own commit in that bundle, and the squash keeps your authorship as Co-authored-by). Thank you!

@lidge-jun lidge-jun closed this Oct 16, 2026
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.

2 participants