Skip to content

fix(anthropic): eject an OAuth account whose access token was revoked - #6832

Merged
lidge-jun merged 2 commits into
devfrom
codex/n2-6749-revoked-token
Oct 9, 2026
Merged

lidge-jun merged 2 commits into
devfrom
codex/n2-6749-revoked-token

Conversation

@lidge-jun

@lidge-jun lidge-jun commented Oct 9, 2026 •

Copy link
Copy Markdown
Owner

Summary

An Anthropic OAuth account whose access token was revoked now leaves the pool instead of looping on 401. Before this change, account recovery handled only 429 and 403. A 401 with OAuth access token has been revoked. was returned to the client while session affinity kept pinning the same account, so affinity-bound Claude Code sessions repeated the same 401 for hours (#6749).

When a response before any output is an HTTP 401 whose body is exactly {"type":"error","error":{"type":"authentication_error","message":"OAuth access token has been revoked."}} with no error.code (absent or null), the proxy now:

  • marks the sending account needsReauth under the store lock, only if the same credential generation and physical-send ownership are still current, so a re-login that lands in between is never overwritten;
  • clears that account's session affinities;
  • lets an eligible account in the same pool (anthropic or anthropic2, never across pools) take over within the existing per-request and physical-send limits;
  • with no eligible replacement, returns the original 401, and the account stays excluded until the user logs in again.

The body is read bounded, with fatal UTF-8 decoding. Truncated, malformed, timed-out, or cancelled reads, other 401 messages, and any 401 after output has started keep their existing behavior; nothing is marked and nothing is replayed after output. The path covers native Messages (JSON and SSE, including with proactive pooling off, where the recovery candidate is now considered before ordinary admission and must still pass eligibility, selection-revision, and route checks), translated dispatch and continuation, and the web-search and image sidecars (their early gate now admits an Anthropic 401 next to 403). No account is refreshed or deleted, and no body, token, or account identifier is logged; recovery telemetry reuses the existing oauth-401 kind.

Closes #6749

Verification

  • New regression files: tests/adapters/anthropic/anthropic-revoked-token.test.ts (classifier: exact envelope, code/null, wording, truncation, fatal UTF-8, cancellation, generation fencing), tests/claude-integration/messages-revoked-token.test.ts (native JSON/SSE, both pools, pool on and off, no sibling, post-output), anthropic-revoked-token-continuation.test.ts, anthropic-revoked-token-sidecars.test.ts, and anthropic-revoked-token-boundaries.test.ts (queued-writer races: UUID, cancellation, quota epoch, remove/re-add, disabled Pool 2, post-output search). 117 pass; before the source change the first four fail 50 of 106.
  • Existing related files (every test importing the refusal, ownership, routing, sidecar, continuation, or native-OAuth modules, plus tests/oauth/oauth-anthropic-*): 885 pass across 33 files. messages-native, messages-native-oauth, test-layout, test-layout-tooling, file-size-ratchet, core-lab-boundary: 130 pass.
  • bun run typecheck, bun run structure:check, bun run privacy:scan, and docs-site bun run build (569 pages) pass.
  • Security review: the design passed an independent security re-audit, and this PR head gets a separate independent security review before merge. Detailed race analysis is kept out of the repository.
  • The full suite was not run locally (sweep lane with concurrent worktrees); it is left to hosted CI on this head. No live revoked account was exercised.

Checklist

  • Scope stays focused and avoids unrelated cleanup.
  • Docs or release notes were updated when needed. (structure/providers/anthropic-account-pool.md and five other owning structure docs; Claude Code guide in 8 languages)
  • Security-sensitive changes were reviewed for secrets, auth, and unsafe defaults. (Auth change: credential state is marked only under a generation and ownership fence; no new logging or serialization of secrets.)

Summary by CodeRabbit

  • New Features
    • Anthropic account pools can now recover from a revoked OAuth token by retrying with another eligible account before output begins.
    • Accounts with revoked tokens are marked for reauthentication and excluded from future requests until they’re reconnected. If no eligible account is available, the original 401 error is returned.
  • Documentation
    • Updated Claude Code and account-pool guides in multiple languages with the recovery behavior and its limits.

@lidge-jun
lidge-jun requested a review from Ingwannu as a code owner October 9, 2026 11:45
@coderabbitai

coderabbitai Bot commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

📝 Walkthrough

Walkthrough

The change adds recovery for Anthropic OAuth accounts that return an exact revoked-token 401 before output begins. It marks the sending account for reauthentication and supports eligible retries across native Messages, Responses, continuation, search, and image paths. Tests and documentation cover the recovery conditions and limits.

Changes

Anthropic OAuth account recovery

Layer / File(s) Summary
Recognize revoked tokens and update account state
src/oauth/anthropic-account-refusal.ts, src/oauth/anthropic-send-ownership.ts, src/oauth/store.ts, src/oauth/anthropic-routing.ts, tests/adapters/anthropic/anthropic-revoked-token*.test.ts, tests/adapters/anthropic/anthropic-revoked-token-boundaries.test.ts
The refusal handler accepts only a bounded, readable HTTP 401 body with the required error fields and exact message. Generation and send-ownership checks guard the reauthentication update. Successful handling clears session affinity and selects an alternate only when retry is allowed. Tests cover invalid responses, ownership changes, cancellation, account-state races, and persistence failures.
Enable recovery across request paths
src/server/messages-native*.ts, src/server/responses/adapter-*.ts, src/server/responses/sidecar-execution.ts, src/images/loop.ts, src/web-search/loop.ts, tests/claude-integration/messages-revoked-token.test.ts, tests/adapters/anthropic/anthropic-revoked-token-continuation.test.ts, tests/adapters/anthropic/anthropic-revoked-token-sidecars.test.ts
Native Messages, translated Responses, continuations, search, and image paths now admit Anthropic 401 responses to account recovery. Successful 401 retries use the oauth-401 recovery label. Tests cover output commitment, route eligibility, sibling availability, and instance isolation.
Document and register recovery coverage
docs-site/src/content/docs/*/guides/claude-code.md, structure/data-planes/*, structure/providers-and-adapters.md, structure/providers/anthropic-account-pool.md, structure/transports/*, scripts/test-layout/layout.json, tests/fixtures/test-layout-expected.json
The provider, transport, data-plane, and Claude Code guides describe the revoked-token recovery conditions and limits. Test-layout mappings include the new regression tests.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Bug fix · Severity of issue fixed: Medium

Sequence Diagram(s)

sequenceDiagram
  participant Client as Claude Code
  participant Messages as handleNativeMessages
  participant Recovery as rotateAnthropicAccountOnResponseForInstance
  participant Store as markAccountNeedsReauthIfGeneration
  participant Binding as resolveNativeOAuthBindingForInstance
  Client->>Messages: Send request
  Messages->>Recovery: Process exact revoked-token 401
  Recovery->>Store: Mark sending account for reauthentication
  Recovery->>Recovery: Clear affinity and select eligible alternate
  Recovery-->>Messages: Return recovery candidate
  Messages->>Binding: Check candidate selection and model route
  Binding-->>Messages: Return eligible account binding
  Messages-->>Client: Retry request or return original 401
Loading

Suggested reviewers: luvs01


Merge Risk: ⚪ Minimal · up to f84a7

The changed recovery remains gated before output, with no established issue requiring a fix before merge.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 31.71% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 41 functions across 16 files. (16 skipped… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title clearly and concisely describes the main change: excluding an Anthropic OAuth account after its access token is revoked. It matches the documented failover and reauthentication behavior.
Linked Issues check Passed Issue #6749 is an active direct target. The implementation meets its coding requirements. In src/oauth/anthropic-account-refusal.ts, isRevokedOAuthToken requires status 401, bounded fatal-UTF-8 bo…
Out of Scope Changes check Passed The changes stay within issue #6749. The changes in src/oauth/anthropic-account-refusal.ts, src/oauth/anthropic-routing.ts, src/oauth/anthropic-send-ownership.ts, and src/oauth/store.ts provid…

Full details: Docstring Coverage

Explanation

Docstring coverage is 31.71% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 41 functions across 16 files. (16 skipped: 16 unsupported.)



  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR

🧪 Generate unit tests (beta)
  • Commit to this branch
  • 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.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 9, 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-09T11:48:35.978255Z adcf271 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 9, 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 9, 2026
Account recovery handled only 429 and 403, so an exact revoked-token 401 was returned to the client while session affinity kept pinning the same account. Before any output, classify that exact envelope (bounded, fatal UTF-8, no error code), mark the sending account needsReauth under a credential-generation and send-ownership fence, clear its affinities, and allow an eligible same-pool account to take over within existing send limits. Covers native Messages (including pool-off admission order), translated dispatch and continuation, and the web-search and image sidecars. Other 401s and anything after output keep their existing behavior.

Closes #6749
@lidge-jun
lidge-jun force-pushed the codex/n2-6749-revoked-token branch from f096fa7 to f84a73f Compare October 9, 2026 13:20

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔇 Additional comments (32)
src/oauth/anthropic-account-refusal.ts (1)

110-131: LGTM!

src/oauth/anthropic-routing.ts (1)

707-707: LGTM!

src/oauth/anthropic-send-ownership.ts (1)

35-36: LGTM!

src/oauth/store.ts (1)

1511-1511: LGTM!

tests/adapters/anthropic/anthropic-revoked-token.test.ts (1)

1-234: LGTM!

tests/adapters/anthropic/anthropic-revoked-token-boundaries.test.ts (1)

1-110: LGTM!

src/server/messages-native-oauth.ts (1)

142-155: LGTM!

src/server/messages-native.ts (1)

516-516: LGTM!

Also applies to: 688-688, 706-706

src/server/responses/adapter-continuation.ts (1)

404-404: LGTM!

Also applies to: 431-431

src/server/responses/adapter-dispatch.ts (1)

1100-1100: LGTM!

Also applies to: 1125-1125

src/server/responses/sidecar-execution.ts (1)

207-207: LGTM!

Also applies to: 371-371

src/images/loop.ts (1)

707-707: LGTM!

src/web-search/loop.ts (1)

580-581: LGTM!

tests/claude-integration/messages-revoked-token.test.ts (1)

1-215: LGTM!

tests/adapters/anthropic/anthropic-revoked-token-continuation.test.ts (1)

1-127: LGTM!

tests/adapters/anthropic/anthropic-revoked-token-sidecars.test.ts (1)

1-139: LGTM!

docs-site/src/content/docs/guides/claude-code.md (1)

84-89: LGTM!

docs-site/src/content/docs/ja/guides/claude-code.md (1)

634-634: LGTM!

docs-site/src/content/docs/ko/guides/claude-code.md (1)

697-697: LGTM!

docs-site/src/content/docs/ru/guides/claude-code.md (1)

669-669: LGTM!

docs-site/src/content/docs/tr/guides/claude-code.md (1)

891-891: LGTM!

docs-site/src/content/docs/zh-cn/guides/claude-code.md (1)

600-600: LGTM!

docs-site/src/content/docs/zh-tw/guides/claude-code.md (1)

676-676: LGTM!

structure/data-planes/images.md (1)

25-26: LGTM!

structure/data-planes/inbound-compat.md (1)

519-520: LGTM!

structure/providers-and-adapters.md (1)

10-10: LGTM!

structure/providers/anthropic-account-pool.md (1)

301-322: LGTM!

structure/transports/inventory.md (1)

46-46: LGTM!

structure/transports/responses-failover.md (1)

545-545: LGTM!

scripts/test-layout/layout.json (1)

7-11: LGTM!

tests/fixtures/test-layout-expected.json (1)

4-8: LGTM!

docs-site/src/content/docs/fr/guides/claude-code.md-779-779 (1)

779-779: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

⚠️ Unverified finding
Verification ran but could not confirm this finding. It is shown for review, not as a verified issue.

Wrap code identifiers in backticks to fix the false typographic warning.

On Line 779, the identifiers authentication_error and error.code are plain text. LanguageTool reads error.code as a sentence break and asks for a space after the period. The English source uses backtick formatting for these identifiers. Add backticks here so the French page matches the English page and the period stays part of the identifier.

Proposed fix
-Uniquement avant toute sortie : Un HTTP 401 authentication_error (sans error.code) portant
+Uniquement avant toute sortie : un HTTP 401 `authentication_error` (sans `error.code`) portant

The same plain-text identifiers also appear on the ja, ko, ru, tr, zh-cn and zh-tw pages. Apply the same change there.

Source: Linters/SAST tools


ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Repository: lidge-jun/opencodex/.coderabbit.yaml
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: 93dd7b0d-d15a-4a5e-9a3c-d80ffc0550fa
📥 Commits

Reviewing files that changed from the base of the PR and between 4504a56 and f84a73f.

📒 Files selected for processing (32)
  • docs-site/src/content/docs/fr/guides/claude-code.md
  • docs-site/src/content/docs/guides/claude-code.md
  • docs-site/src/content/docs/ja/guides/claude-code.md
  • docs-site/src/content/docs/ko/guides/claude-code.md
  • docs-site/src/content/docs/ru/guides/claude-code.md
  • docs-site/src/content/docs/tr/guides/claude-code.md
  • docs-site/src/content/docs/zh-cn/guides/claude-code.md
  • docs-site/src/content/docs/zh-tw/guides/claude-code.md
  • scripts/test-layout/layout.json
  • src/images/loop.ts
  • src/oauth/anthropic-account-refusal.ts
  • src/oauth/anthropic-routing.ts
  • src/oauth/anthropic-send-ownership.ts
  • src/oauth/store.ts
  • src/server/messages-native-oauth.ts
  • src/server/messages-native.ts
  • src/server/responses/adapter-continuation.ts
  • src/server/responses/adapter-dispatch.ts
  • src/server/responses/sidecar-execution.ts
  • src/web-search/loop.ts
  • structure/data-planes/images.md
  • structure/data-planes/inbound-compat.md
  • structure/providers-and-adapters.md
  • structure/providers/anthropic-account-pool.md
  • structure/transports/inventory.md
  • structure/transports/responses-failover.md
  • tests/adapters/anthropic/anthropic-revoked-token-boundaries.test.ts
  • tests/adapters/anthropic/anthropic-revoked-token-continuation.test.ts
  • tests/adapters/anthropic/anthropic-revoked-token-sidecars.test.ts
  • tests/adapters/anthropic/anthropic-revoked-token.test.ts
  • tests/claude-integration/messages-revoked-token.test.ts
  • tests/fixtures/test-layout-expected.json

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

@lidge-jun
lidge-jun merged commit 32a25c8 into dev Oct 9, 2026
34 of 35 checks passed
@lidge-jun
lidge-jun deleted the codex/n2-6749-revoked-token branch October 9, 2026 13:42
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