Skip to content

Poll the usage endpoint every 5 minutes and back off on 429 - #7

Merged
HandyS11 merged 1 commit into
mainfrom
fix/usage-endpoint-rate-limit
Nov 1, 2026
Merged

HandyS11 merged 1 commit into
mainfrom
fix/usage-endpoint-rate-limit

Conversation

@HandyS11

@HandyS11 HandyS11 commented Nov 1, 2026

Copy link
Copy Markdown
Owner

Fixes #6

Problem

The plugin polled https://api.anthropic.com/api/oauth/usage every 60s with only a 30s server cache, so every poll reached the endpoint. That endpoint allows only a handful of requests per window, and the budget is shared with Claude Code's own /usage screen and statusline (see anthropics/claude-code#30930 and anthropics/claude-code#31637). Users hit HTTP 429 and /usage became unusable until the cooldown passed. A 429 was also never cached or backed off, so the next poll retried a minute later.

Fix

  • Client: re-fetches every 5 minutes. A separate one-minute tick re-renders the countdown and "updated Xm ago" text without any network call. The refresh button still works but cannot bypass the server cache.
  • Server: new LiveCache (src/live.ts) calls the endpoint at most once every 5 minutes, coalesces concurrent callers into one request, honors Retry-After on 429, and otherwise backs off 5 → 10 → 20 → 40 → 60 minutes. A success resets the backoff. Non-429 failures retry at the normal cadence. Credential errors (missing or expired token) are checked locally first and stay unthrottled, so a fresh login shows up immediately.
  • UI: while backing off, the last good reading is still shown, marked stale with the reason and its age, instead of blanking the gauges.
  • README: documents the cadence and rate-limit behavior.

Testing

  • 12 new tests in src/live-cache.test.ts (written first, watched fail) cover the cache window, request coalescing, 429 with and without Retry-After, exponential backoff with reset on success, stale serving, and throttling after non-429 errors. Full suite: 33 tests passing.
  • Smoke test against the real server: first GET /live hit the endpoint, second returned from cache with the same fetchedAt.

🤖 Generated with Claude Code

The /api/oauth/usage endpoint allows only a handful of requests per
window, and that budget is shared with Claude Code's own /usage screen.
Polling it every 60s (with a 30s server cache) tripped the limit and
made /usage unusable until the cooldown passed.

- Client re-fetches every 5 minutes; countdown text re-renders every
  minute without a network call.
- Server caches for 5 minutes, coalesces concurrent callers into one
  request, honors Retry-After on 429, and otherwise backs off
  5/10/20/40/60 minutes. Credential errors stay unthrottled.
- While backing off, the last good reading is served and shown as
  stale with the reason and its age.
- README documents the rate-limit behavior.

Fixes #6

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings November 1, 2026 19:58

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟢 Approval recommended

No objective defects were identified, and the rate-limit fix's new caching and backoff logic is well-scoped and thoroughly covered by the added tests.

No open issues 🎉

What changed in this PR

This PR fixes issue #6, where the plugin polled the rate‑limited https://api.anthropic.com/api/oauth/usage endpoint every 60s (with only a 30s cache), causing HTTP 429 errors that made /usage unusable. It introduces a server‑side LiveCache that enforces a 5‑minute polling cadence, coalesces concurrent callers, and backs off on 429, while the client slows network polling to 5 minutes and keeps a lightweight 1‑minute tick for re‑rendering countdown/"updated" text. The UI now keeps showing the last good reading (marked stale) during backoff instead of blanking the gauges.

Changes:

  • New LiveCache in src/live.ts with 5‑minute caching, request coalescing, Retry-After‑aware 429 handling, and exponential backoff (5 → 10 → 20 → 40 → 60 min) that resets on success.
  • Server refactored to check credentials locally (unthrottled) before serving the cache; client re‑fetches every 5 min and re‑renders every minute without network calls, surfacing stale readings in the UI.
  • 13 new tests in src/live-cache.test.ts plus README documentation of the new cadence and rate‑limit behavior.
File Description
src/​live.ts Adds LiveCache, backoffMs, parseRetryAfter, LIVE_CACHE_MS, and the UsageResponse/LiveResult types that implement caching and 429 backoff.
src/​server.ts Extracts readCredentials() as an unthrottled local check and wires /live through the new LiveCache instead of the ad‑hoc 30s cache.
src/​index.ts Splits polling (5 min network) from rendering (1 min tick), extends LiveData with stale/error/retry fields, and renders the stale "last reading" note and "updated Xm ago" text.
src/​live-cache.test.ts New unit tests covering cache window, coalescing, 429 with/without Retry-After, backoff reset, stale serving, and non‑429 error handling.
README.md Documents the 5‑minute cadence and the rate‑limiting/backoff behavior.

I reviewed the caching and backoff logic in detail (concurrency coalescing in get(), the result() null-safety invariant, backoff reset on success, and parseRetryAfter edge cases), verified there are no dangling references to the removed 30s cache path, and confirmed the new behavior matches the tests and README. I did not find objective defects worth flagging — the change is well-scoped and the new logic is thoroughly covered by tests.

Review effort: Balanced

@HandyS11
HandyS11 merged commit 8a1f1ba into main Nov 1, 2026
3 checks passed
@HandyS11
HandyS11 deleted the fix/usage-endpoint-rate-limit branch November 1, 2026 20:38
HandyS11 added a commit that referenced this pull request Nov 1, 2026
Includes the usage endpoint rate-limit fix (#7) and the host theme
restyle (#8).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

插件每一分钟请求一次claude的ai接口,会导致429报错

2 participants