Skip to content

[Bug]: Native Claude reset-credit read retries /api/oauth/usage every 5 minutes after a 429 and ignores Retry-After, so the account stays rate-limited #37053

Closed as not planned

Description

@Stebulous

Before submitting

  • I searched existing issues and did not find a duplicate.
  • I included enough detail to reproduce or investigate the problem.

Area

apps/server

Steps to reproduce

  1. Sign in to Claude Code with one Pro or Max login, and use the native Claude provider in T3 Code.
  2. Keep T3 Code open while other clients on the same login also read GET https://api.anthropic.com/api/oauth/usage (Claude Code itself, or a usage widget).
  3. Wait until the endpoint answers HTTP 429 to any request on the login.
  4. Read the server traces in ~/.t3/userdata/logs/server.trace.ndjson* for url.full = https://api.anthropic.com/api/oauth/usage?cedar_ember=1&skip_spend=1 (the readClaudeResetCredits span).

Expected behavior

After a 429, the reset-credit read waits until the Retry-After time before it asks again, and keeps the last good reset data in the meantime.
The wait survives a server restart, and every route that reads the endpoint shares it.

Actual behavior

The reset-credit read keeps its 5-minute cadence through the 429:

Local time (EST) Request Status
02:14:42 GET /api/oauth/usage?cedar_ember=1&skip_spend=1 200
02:19:43 same 429
02:24:44 same 429
02:34:45 same 429

Another client on the same login honors Retry-After:

  • Its request at 02:27:51 got 429 with a wait until 03:19:42. That is exactly one hour after T3 Code's first 429 at 02:19:43.
  • Its next request, at 03:22:30, after that wait ended, got 429 again with a new wait of exactly 3600 seconds.

The endpoint seems to block the whole login for one hour, and the retries inside the block appear to keep the login over the limit.
Every client on the login then loses the endpoint for hours, including Claude Code's own usage reads.

On main (e2afd25), readClaudeResetCredits in apps/server/src/provider/claudeResetCredits.ts still has no 429 branch.
filterStatusOk fails on the 429, and Effect.orElseSucceed(() => undefined) turns the failure into "no reset data", so the caller can't tell a rate limit from an empty balance and has no wait time to honor.

Impact

Minor bug or occasional failure

Version or commit

T3 Code (Beta) 0.1.8 desktop. Claude Code 2.1.322.
The reader is the same on main at e2afd25 (after 0.1.9).

Environment

Windows 11, one Claude Pro login, native Claude provider. No CLIProxyAPI hub.

Logs or stack traces

{"name":"GET","attributes":{"url.full":"https://api.anthropic.com/api/oauth/usage?cedar_ember=1&skip_spend=1","http.response.status_code":200},"exit":{"_tag":"Success"}}
{"name":"GET","attributes":{"url.full":"https://api.anthropic.com/api/oauth/usage?cedar_ember=1&skip_spend=1","http.response.status_code":429,"error.type":"429"},"exit":{"_tag":"Failure","cause":"HttpClientError: StatusCode error (429 GET https://api.anthropic.com/api/oauth/usage)"}}

The spans start at 02:14:42, 02:19:43, 02:24:44, and 02:34:45 local time on 2026-11-14.
The traces are sampled, so the table can miss requests.

Related

Suggested behavior

  1. On 429, read Retry-After and send no reset-credit read before that time. Without the header, wait at least 15 minutes.
  2. Keep the last good reset data while rate-limited.
  3. Share the wait with the other /api/oauth/usage routes in [Bug]: Native Claude provider: usage probes hit /api/oauth/usage with no back-off and drop the last good reading (429s) #27772, so that one throttle covers the login.

Workaround

None known in T3 Code.

Activity

  1. juliusmarminge commented on Nov 15, 2026

    @juliusmarminge
    Member

    I tried to reproduce this on the current build and could not.

    Not reproduced in a real run of the app: that needs a first-party Claude Pro/Max OAuth login, which no VM has. On origin/main 11c425b I added a test to the existing apps/server/src/provider/claudeResetCredits.test.ts: a fake HttpClient answers the first readClaudeResetCredits call with 200 and one grant, the second with 429 and retry-after: 3600, and fails if a third request is sent. vp test run src/provider/claudeResetCredits.test.ts from apps/server: 8 pass, the new test fails with 'must not send a request inside the Retry-After window', so the third read goes out right after the 429. The test also expects that third read to return the last good grant. Pushed as factory/repro-37053 (63c9aa9).

    Closing as not planned. If it still happens for you, reply with the exact steps and the version you are on and it will be looked at again.

  2. Stebulous commented on Nov 15, 2026

    @Stebulous
    Author

    Thanks for the quick test. I think the failing test is the reproduction. It checks for the expected behavior, and on main (11c425b4b6) it fails because the third read goes out right after the 429. That is the bug in this report.

    The real-app evidence is the trace table above, from T3 Code 0.1.8 desktop with a Claude Pro login on Windows 11. The reader is unchanged on main. Could you reopen this? factory/repro-37053 (63c9aa9d86) is already a regression test for a fix.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions