fix(server): preserve hub usage readings after provider rate limits#19820

open20
main
GitHub fork: fix/limits-hub-probe-throttle
opened8d agobyJJustYannicc
View on GitHubTwo-way sync enabledPR last imported 1d ago

Problem

Repeated Limits refreshes can make the hub hit Claude’s usage endpoint with bursts of requests. A provider 429 then replaces valid usage bars with an unavailable message. See #19807.

Change

Keep per-account pacing inside the hub API: successful probes are reused for one minute, consecutive failures back off for two, four, then eight minutes, and failed probes retain a successful reading until it is fifteen minutes old. Account identity includes the hub URL, file, provider, email, and workspace. Reset-credit attempts invalidate the account’s cached reading after success, uncertain failure, or interruption; an explicit provider rejection with 429 preserves the reading and backoff.

Rebuilt from @JustYannicc’s original contribution, preserving the contributor commits and merging main. Removed the generic throttled-reader abstraction and consolidated sixteen added tests into five behavior cases in the existing API suite. Management diagnostics and URL-prefix behavior already exist on main and are unchanged here.

Two rounds of independent Opus and Sol reviews completed; the final round found no confirmed issues. The first round’s refresh-documentation clarification is included.

Scope and approval

This maintainer takeover follows the reported bug in #19807. Maintainer review is still needed for the one-minute refresh, two-to-eight-minute backoff, and fifteen-minute retention policy identified by Macroscope. Unmerged.

Verification

  • Focused suites: 24 passed across the API and usage-source service; targeted lint, formatting, and server-only typecheck pass.
  • All five regression cases fail without the fix; the concurrency case also fails when reset invalidation loses coordination with in-flight reads.
  • Headless HTTP before/after proof uses the actual API code, a local simulated hub, and a controlled clock. Across eight refreshes of two Claude accounts, main makes 16 probes, receives 14 rate limits, and returns 14 blank readings; the rebuilt code makes 6 probes, receives 4 rate limits, and returns no blank readings.

This changes the shared server path consumed by web, desktop, and mobile. No UI rendering or wire contract changed. The HTTP proof uses fixture accounts, not live provider credentials. Actual upstream timing, real credit redemption, macOS, Windows, Electron desktop, iOS, and Android were not verified. The existing Codex fallback when usage succeeds but its separate credit lookup fails remains unchanged; the combined partial result may now be reused for one minute.

CI on ef207fca70: lint, build/typecheck, package tests, and all eight server shards passed. Web, Rust, native static analysis, and release smoke jobs skipped for this server-only scope. One duplicate fingerprint run was cancelled; another fingerprint run on the same head succeeded. CodeRabbit completed a full review of the rebuilt head with no actionable findings, following two independent delegated whole-PR review rounds. There are no unresolved review threads.

Implementation: Codex GPT-6.1-Sol (high), tests: Codex GPT-6-Astra (high), independent reviews: Claude Opus 5.5 and Codex GPT-6-Sol, through T3 Code’s delegated agents and Codex harness.

Fixes #19807 GitCafe: https://git.cafe/pingdotgg/t3code/issues/19807

Checks

All checks passed

Added 1 commit

  • fix(server): hub Limits keep Claude accounts' bars when usage reads hit 42972eef04
Synced from GitHub

Approvability

Verdict: Needs Human Approval

Macroscope's review found this PR not approvable — The change adds a stateful per-account throttling and stale-data policy to an existing production usage path, altering refresh frequency, error handling, and the freshness of displayed limits. Its hard-coded pacing and retention values become the default behavior for hub accounts, warranting human review despite the focused tests.

You can add or adjust custom eligibility rules. Learn more.

Synced from GitHub

Review in Change Stack →

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

  • Last reviewed commit: ef207fca709b4ddcd9115376c7000e0ae6d56cdf
  • Latest commit, not reviewed: df2da49b4f2482cb82e134a6b645f90c3d42029a

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Path: .coderabbit.config.ts
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 2ebd3c23-277f-4d0c-a958-b1170e11aced
📥 Commits

Reviewing files that changed from the base of the PR and between 1451e7cb23034d29774338608368a15d8845df36 and ef207fca709b4ddcd9115376c7000e0ae6d56cdf.

📒 Files selected for processing: 3
  • apps/server/src/usage/cliproxyApi.test.ts
  • apps/server/src/usage/cliproxyApi.ts
  • docs/user/usage.md

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

📝 Walkthrough

CLIProxyAPI usage reads now use per-account pacing and caching, with provider rate limits reported separately. Credit redemption clears cached readings based on its outcome. Tests and usage documentation cover the updated behavior.

Changes: CLIProxyAPI usage reads

Layer / File(s)Summary
Throttled usage reads
apps/server/src/usage/cliproxyApi.ts, apps/server/src/usage/cliproxyApi.test.ts, docs/user/usage.md
Usage reads are keyed by hub and account identity. Successful reads schedule the next probe after 60 seconds; failed reads use increasing intervals up to eight minutes. The last successful reading remains available for up to fifteen minutes. Tests and documentation cover pacing, retention, identity separation, and provider 429 messages.
Redemption and usage cache
apps/server/src/usage/cliproxyApi.ts, apps/server/src/usage/cliproxyApi.test.ts
Redemption clears the affected account’s cached reading on completion or interruption, except when redemption returns the provider rate-limit error. Tests cover concurrent reads and redemption outcomes.

Priority: ➖ Normal

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

Change: Bug fix

Sequence Diagram(s)

mermaid
1sequenceDiagram
2 participant UsageConsumer
3 participant CLIProxyAPI
4 participant CLIProxyAPIHub
5 UsageConsumer->>CLIProxyAPI: request account usage
6 alt probe is not scheduled
7 CLIProxyAPI-->>UsageConsumer: return cached reading or unavailable usage
8 else probe is scheduled
9 CLIProxyAPI->>CLIProxyAPIHub: request account usage
10 CLIProxyAPIHub-->>CLIProxyAPI: return usage or provider error
11 CLIProxyAPI-->>UsageConsumer: return reading or unavailable usage
12 end

Suggested reviewers: juliusmarminge

Merge Risk: ⚪ Minimal · up to ef207

No confirmed issue blocks merging the usage-pacing change; the freshness policy still awaits maintainer approval.

Security architecture risk: 🔵 Low · up to ef207

The change affects usage freshness and reset-credit recovery without adding permissions or new external destinations. Account checks and recovery ordering remain intact. Remaining uncertainty concerns long-running state accumulation and provider behavior during rate limiting.

Retained concerns No architecture-level concerns identified.

Security review details

Security Blast Radius

  • observed — The new mutable state is shared within the server usage service across configured hubs. Keys separate hub URL, account file ID, provider, email, and workspace, limiting ordinary cache reuse and invalidation to that identity. It stores account usage information, not provider bearer tokens.

Trust Boundaries and Controls

  • observed — Each account read still obtains an authenticated account listing from the configured hub before consulting cached usage, and disabled or unsupported accounts are filtered out. Hub-supplied account metadata determines the cache identity and provider auth index, while outbound provider destinations remain fixed by server code. These trust relationships predate the PR.

Resilience and Maintainability Implications

  • observed — The recovery design conservatively discards cached readings after uncertain credit outcomes. Cleanup waits for older same-identity reads, preventing them from restoring pre-redemption state after invalidation. Added behavior tests cover pacing, identity isolation, concurrent reads, provider rejection, uncertain failure, and interruption.

Hardening Proposals

  • proposed — Consider bounded eviction or pruning of inactive identities when accounts or configured sources disappear. This would bound long-running memory consumption and residual account metadata without treating the current behavior as a demonstrated security finding.
Pre-merge checks: Passed 3 · Failed 1

Failed Failed checks: 1 warning

Check nameStatusExplanationResolution
Description checkWarningThe description covers the problem, change, scope, and verification. However, it does not provide the explicit maintainer approval required for the proposed freshness policy; it says that approval is …Add a link to a maintainer comment that explicitly approves the one-minute refresh, two-to-eight-minute backoff, and fifteen-minute retention policy, or document the maintainer discussion and approval in the description.
Passed Passed checks: 3 passed
Check nameStatusExplanation
Title checkPassedThe title clearly and concisely describes the main change: preserving hub usage readings after provider rate limits.
Linked Issues checkPassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes checkPassedCheck skipped because no linked issues were found for this pull request.
Full details: Description check

Explanation

The description covers the problem, change, scope, and verification. However, it does not provide the explicit maintainer approval required for the proposed freshness policy; it says that approval is still pending.

Resolution

Add a link to a maintainer comment that explicitly approves the one-minute refresh, two-to-eight-minute backoff, and fifteen-minute retention policy, or document the maintainer discussion and approval in the description.

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

Comment @coderabbitai help to get the list of available commands.

Added 6 commits

  • Merge commit '4d679a63244e0f6a2118df6a6488ed1295a87797' into upstream/pr-19820-maintain3e78ed7
  • fix(server): hub usage pacing keeps forget atomic and drops stale entries1006a51
  • fix(server): hub usage readings follow the credential and survive interrupted redeems54c8cf9
  • fix(server): hub usage reads of one account run one at a time7d6cc5b
  • fix(server): a hub credential replaced with another provider gets its own reading122af9b
  • Merge remote-tracking branch 'upstream/main' into fix/limits-hub-probe-throttle41e74e1

Taking over this one as maintainer. I’ll keep this on the original PR, merge current main, reduce the tests to the observable pacing/cache/reset behavior, and rebuild the implementation where a smaller solution fits. I’ll run independent adversarial reviews and verification before pushing. Thanks @JustYannicc for the reproduction and keyed pacing/reset work.

Added 4 commits

  • Merge remote-tracking branch 'origin/main' into fix/limits-hub-probe-throttle9ea470e
  • test(server): consolidate hub usage pacing coverage047769f
  • fix(server): keep hub usage pacing in the APIe617bf5
  • docs(usage): explain paced hub refreshesef207fc

The scope warning about management diagnostics and URL-prefix handling refers to code already present on main. The current diff changes only the pacing/cache and reset invalidation behavior, its five regression cases, and usage guidance. Both independent full-diff reviews verified this. The body now follows the repository template and links #19807; approval of the freshness policy remains explicitly pending rather than being inferred from the takeover. CI passes on ef207fca70. The latest CodeRabbit code review was deferred by the shared organization review quota, so I am retaining that limitation in the description.

Added 9 commits

  • Merge branch 'main' into fix/limits-hub-probe-throttlefe7825b
  • Merge branch 'main' into fix/limits-hub-probe-throttlef3c7480
  • Merge branch 'main' into fix/limits-hub-probe-throttle24a6062
  • Merge branch 'main' into fix/limits-hub-probe-throttlec43c402
  • Merge branch 'main' into fix/limits-hub-probe-throttle154a1c1
  • Merge branch 'main' into fix/limits-hub-probe-throttlebf7ecf3
  • Merge branch 'main' into fix/limits-hub-probe-throttle534269c
  • Merge branch 'main' into fix/limits-hub-probe-throttle202baf3
  • Merge branch 'main' into fix/limits-hub-probe-throttledf2da49
Synced from GitHub

@juliusmarminge could this get a maintainer sign-off soon? It's hitting us every day.

Our setup: two Claude accounts behind CLIProxyAPI v8.0.36, iOS app 2.0.0 (details in my comment on #19807). Both accounts' oauth/usage probes come back 429 through the hub, and the Limits tab on mobile goes completely blank. That leaves us with no way to see how much quota we have left. Every refresh, from the phone or desktop, adds another round of requests and makes it worse.

The 1-minute reuse, the backoff and keeping the last good reading are exactly what's missing. The proposed timings look fine to us from the user side, and we'd much rather have this shipped than wait on fine-tuning the numbers. CI is green and your rebuild has already been reviewed. Happy to test a nightly build as soon as it lands. Thanks for picking this up!