[Bug]: claude-sdk-oauth auth failure is stored as blockReason: rate_limit — all accounts blocked while quota is fine, expired blocks never clear
Summary
claude-sdk-oauth reports all accounts blocked (rate limit) and refuses to run, while the Claude subscription is not rate limited at all. claude /usage in Claude Code shows only Fable at its weekly limit this week; Claude models are well under quota and requests succeed there.
The block appears to be an authentication failure misclassified as a rate limit, persisted into the credential-pool sidecar with a fabricated "soonest automatic retry" time. Because it is stamped as rate_limit, the user is told to wait for a reset that will never fix it, and the correct action (re-login) is not the advertised one.
Error as shown in the TUI
Error: All Claude accounts for claude-sdk-oauth are currently blocked (rate limit or auth errors).
Soonest automatic retry: 2026-09-16T08:46:07.773Z.
/claude-account list - inspect account states
/login claude-sdk-oauth - add another account
Session continuity lost - resent the full conversation (query_failed)That block repeats five times, each repetition resending the whole conversation, and then ends with:
Error: Retry failed after 5 attempts: Retry cancelledEvidence from local state
~/.omo/agent/credential-pool-state.json right now (read at 2026-09-16T08:50:05Z):
pool.providers.claude-sdk-oauth.lanes.stored.slots.imported-anthropic.blockReason = rate_limit
pool.providers.claude-sdk-oauth.lanes.stored.slots.imported-anthropic.blockedUntil = 2026-09-16T04:56:51.287Z
pool.providers.claude-sdk-oauth.lanes.stored.slots.imported-anthropic.failureCount = 1
pool.providers.claude-sdk-oauth.lanes.stored.slots.Main.blockReason = rate_limit
pool.providers.claude-sdk-oauth.lanes.stored.slots.Main.blockedUntil = 2026-09-16T05:03:23.746Z
pool.providers.claude-sdk-oauth.lanes.stored.slots.Main.failureCount = 2Two things stand out:
blockReason: rate_limitis wrong. The Claude account is not rate limited; only Fable is at its weekly cap.- Both
blockedUntilvalues are ~4 hours in the past yet the slots are still recorded as blocked. Expired blocks are not being cleared from the sidecar.
~/.omo/agent/auth.json currently carries no blockReason for these slots, so the two health stores disagree: the auth store considers the slots fine while the pool sidecar still holds stale rate_limit blocks.
Suspected mechanism
Traced earlier the same day on this machine: the inner SDK failure was a permanent auth error, not a quota error —
SDK-ts authentication_failed, HTTP 401
Failed to authenticate. API Error: 401 OAuth access token has been revoked.That inner error is surfaced as the generic guidance string "... (rate limit or auth errors)". The outer credential-pool classifier then appears to string-match "rate limit" in that generic message and persists blockReason: rate_limit with a timed blockedUntil. A permanent auth failure is thereby laundered into a temporary quota block.
That also explains the recurrence pattern: clearing the sidecar alone restores service briefly, then the next request re-stamps a rate_limit block, because the underlying credential problem was never surfaced to the user as an auth problem.
Expected
- An auth/401 failure is classified as
auth_error, never asrate_limit, and the message tells the user to re-authenticate instead of quoting a retry time. - The classifier keys off the structured inner error, not off substrings of its own generic guidance text.
- A
blockedUntilin the past is treated as expired and cleared, rather than leaving the slot blocked. - A non-retryable auth failure is not retried 5 times with a full conversation resend each round.
- The two health stores (
auth.jsonblock fields andcredential-pool-state.jsonsidecar) agree, or one is authoritative.
Actual
Correct Claude quota, revoked/failed credential, reported as a rate limit with a retry deadline that does not resolve it; stale expired blocks persist; five full-conversation resends burn cache and tokens before the turn dies.
Environment
- omo-ai
5.0.0-0.beta.65(engine: senpi2026.9.16) - macOS (Darwin 25.5.0), arm64 (Apple M4), Node v24.15.0
- Provider
claude-sdk-oauth, two stored slots (imported-anthropic,Main) which are the same Claude account, not independent quota
Related
Related to #7084 (login appends slots; auth_error never expires), but distinct: this report is about an auth failure being labelled and stored as rate_limit, the stale expired blockedUntil, and the 5x retry/resend loop on a non-retryable error.
Source: code-yeongyu/oh-my-openagent