Skip to content

Fail over org-disabled Claude accounts (403 permission_error)#54

Merged
lawrencecchen merged 3 commits into
mainfrom
fix-claude-403-failover
Jul 4, 2026
Merged

Fail over org-disabled Claude accounts (403 permission_error)#54
lawrencecchen merged 3 commits into
mainfrom
fix-claude-403-failover

Conversation

@lawrencecchen

@lawrencecchen lawrencecchen commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Observed live 2026-07-04 ~09:51 UTC: Anthropic disabled OAuth for one account's org mid-day (403 permission_error 'OAuth authentication is currently not allowed for this organization'); Claude Code renders it as 'Your organization has disabled Claude subscription access for Claude Code'. Failover only reacted to 429/401/rejected, so sticky sessions pinned to that account black-holed every request while 8+ healthy accounts sat idle. The same error also appears in a session transcript from 2026-06-24, so this is a recurring Anthropic-side state, not a one-off.

403 now joins the failover set, marks the account exhausted with the credential TTL (hourly re-probe picks it back up if the org is re-enabled), and the two-commit structure shows the regression test red then green. Also adds the missing 'serving claude fable via fallback chain' log on the handler-level fallback path so Bedrock activity is attributable chain-vs-gateway during monitoring.


View with Codesmith Autofix with Codesmith
Need help on this PR? Tag /codesmith with what you need. Autofix is disabled.


Summary by cubic

Fail over Claude accounts that return 403 permission_error when their org disables OAuth, preventing sticky sessions from black-holing requests and retrying on healthy accounts.

  • Bug Fixes

    • Treat 403 as account-unusable; fail over and mark the account exhausted with the credential TTL; move sticky sessions off the disabled account.
    • Add a regression test that simulates a 403 org-disabled account and verifies retry success and exhaustion marking.
    • Log handler-level fallback ("serving claude fable via fallback chain") when no usable OAuth account is selected, so Bedrock activity is attributable.
  • Dependencies

    • Bump subrouter to 0.1.32 in package.json and pyproject.toml.

Written for commit 2f33d1d. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • Bug Fixes

    • Improved fallback handling for Claude requests so accounts returning 403 are treated as unavailable and retries can move to a healthy account.
    • Prevented some failed Claude sessions from getting stuck on an unusable account.
    • Added a warning log when a fallback response is successfully served.
  • Tests

    • Added coverage for Claude failover when an organization disables OAuth access.
  • Chores

    • Bumped the release version to 0.1.32.

…ble fallback

A 403 permission_error ('OAuth authentication is currently not allowed for
this organization') is account-specific: Anthropic disabled Claude Code
subscription access for one account's org mid-day while the rest of the pool
stayed healthy, and sticky sessions pinned to that account black-holed every
request with 'Your organization has disabled Claude subscription access'.
403 now joins 429/401 in the failover set and marks the account exhausted
with the credential TTL (an org toggle does not self-heal on a rate-limit
schedule; the hourly lapse re-probes it).

Also log 'serving claude fable via fallback chain' from the handler-level
fallback path (selection found no usable OAuth account), which previously
emitted nothing, so Bedrock activity could not be attributed chain-vs-gateway
during monitoring.
@coderabbitai

coderabbitai Bot commented Jul 4, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d0e04def-403d-49d5-9ad9-fbe077589edd

📥 Commits

Reviewing files that changed from the base of the PR and between 11d391e and 2f33d1d.

📒 Files selected for processing (5)
  • internal/proxy/claude_fable.go
  • internal/proxy/claude_failover_test.go
  • internal/proxy/proxy.go
  • package.json
  • pyproject.toml

📝 Walkthrough

Walkthrough

This PR extends Claude account failover logic in proxy.go to treat HTTP 403 responses the same as 401 for credential exhaustion, unusable-status classification, and failover eligibility, adds a warning log for Fable fallback usage, includes a new failover test, and bumps package versions.

Changes

Claude 403 Failover Handling

Layer / File(s) Summary
403 exhaustion/unusable classification
internal/proxy/proxy.go
markAccountExhaustedFromResponse, claudeAccountUnusableStatus, and claudeAccountExhaustedByResponse now treat HTTP 403 the same as 401, marking accounts as credential-exhausted and eligible for failover replay.
Fable fallback warning log
internal/proxy/claude_fable.go
serveClaudeFableFallback logs a warning with reason no_usable_account and the fallback response status when serving a fallback response.
Failover test for 403 org-disabled OAuth
internal/proxy/claude_failover_test.go
New test TestUsageLimitRetryTransportClaudeFailsOverOn403OrgDisabled verifies retry-transport failover to a healthy account, exhaustion marking of the disabled account, and sticky-session reassignment when a 403 permission_error is returned.
Version bumps
package.json, pyproject.toml
Package version incremented from 0.1.31 to 0.1.32 in both files.

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

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant usageLimitRetryTransport
  participant ClaudeAPI
  participant Scheduler

  Client->>usageLimitRetryTransport: RoundTrip(request)
  usageLimitRetryTransport->>ClaudeAPI: forward request (cooked account)
  ClaudeAPI-->>usageLimitRetryTransport: 403 permission_error
  usageLimitRetryTransport->>Scheduler: markAccountExhaustedCredential(cooked)
  usageLimitRetryTransport->>Scheduler: select healthy account (fresh)
  usageLimitRetryTransport->>ClaudeAPI: retry request (fresh account)
  ClaudeAPI-->>usageLimitRetryTransport: 200 OK
  usageLimitRetryTransport-->>Client: 200 OK
Loading

Possibly related PRs

  • manaflow-ai/subrouter#5: Both PRs extend the same Claude failover mechanism in proxy.go and claude_failover_test.go from 429 to 403 handling with sticky-session rerouting verification.
  • manaflow-ai/subrouter#38: Both PRs modify the same Claude account exhaustion/unusable classification and marking logic in proxy.go.
  • manaflow-ai/subrouter#39: Both PRs extend Claude usage-limit failover/retry decisioning in proxy.go and add related failover routing tests.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix-claude-403-failover

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.

@lawrencecchen lawrencecchen merged commit f8d0300 into main Jul 4, 2026
4 of 5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant