fix(server): restore Claude subscription usage through proxies#12069

merged5
main
GitHub fork: fix/claude-proxy-usage-header
openedSep 16, 2026byKKanaliseren
View on GitHubTwo-way sync enabledPR last imported 9d ago

Claude subscription usage requests forwarded through CLIProxyAPI could receive an upstream 429 and leave Limits without Claude windows. The existing read-only Claude OAuth usage request now includes User-Agent: claude-code/2.1.273 in the header forwarded upstream. Management authentication, Codex requests, quota calculation and credit consumption are unchanged.

Thanks to @Kanaliseren for the original diagnosis, fix and reported 429-to-200 comparison in #12069. Contributor history is preserved, with main integrated by merge. The takeover folds the duplicate header test into the existing Claude window-mapping fixture, reducing the focused file from 19 cases to 18. The fixture rejects requests missing the Claude client header and checks the resulting mapped windows, without pinning its assertion to a version string.

Validation at ca30fa5: all 18 focused tests, server typecheck, changed-file lint and formatting passed. Earlier red/green proof removed the header and produced one failed mapping test with 17 passing; restoring it returned all 18 to green. The affected source/test blobs are unchanged by the merge. Fresh independent full Claude Opus 5.5 and GPT-6.1 Sol reviews found no code blockers.

The fixture validates serialized header delivery and existing response mapping, not a real proxy hub or Anthropic acceptance. The live 429-to-200 result is the contributor's report, not independently repeated account proof. Upstream acceptance of this fixed client identifier can change. This is headless request/response behavior, so screenshots do not apply. Tests and typecheck used this worktree's dependencies; lint may have resolved shared plugin dependencies through an ignored sibling-worktree link. No tracked dependency change is included.

Current-head CI and the existing human usage/metering approval remain landing gates; this publication does not grant either.

Implementation: Claude Fable 5.1; tests and independent full reviews: Claude Opus 5.5 and GPT-6.1 Sol through T3 Code, using Claude Code and Codex.

Checks

21 successful · 11 skipped
All checks passed
ca30fa5

Added 1 commit

  • fix(server): restore Claude subscription usage through proxiesfddb83c
Synced from GitHub

Approvability

Verdict: Needs Human Approval

Macroscope's review found this PR requires human approval — This focused fix changes the production Claude OAuth usage request so subscription limits are returned through proxies instead of being rejected, with a test covering the required header. Because it affects subscription usage/metering behavior, the change warrants human review despite its small scope.

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

Synced from GitHub

Review in Change Stack →

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: 746de8b3-c62a-4bf3-87f0-1f44c047d78f
📥 Commits

Reviewing files that changed from the base of the PR and between fddb83c5d0e2441d3acddd11ebc8ee5c86befc29 and eee96ed5c92a5d622b376394822dd2073a8b5819.

📒 Files selected for processing: 2
  • apps/server/src/usage/cliproxyApi.test.ts
  • apps/server/src/usage/cliproxyApi.ts

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

📝 Walkthrough

The Claude usage request now includes User-Agent: claude-code/2.1.273. The test mock returns HTTP 200 only when the request’s User-Agent starts with claude-code/; otherwise, it returns HTTP 429.

Changes: Claude usage request handling

Layer / File(s)Summary
Usage request and validation
apps/server/src/usage/cliproxyApi.ts, apps/server/src/usage/cliproxyApi.test.ts
The Claude API call includes the Claude Code User-Agent. The test mock checks its prefix and returns HTTP 200 or 429 based on whether it matches.

Priority: ➖ Normal

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix

Suggested reviewers: juliusmarminge

Merge Risk: ⚪ Minimal · up to eee96

The Claude usage request now identifies itself as Claude Code, and its test checks that the header is present. No merge-blocking issue is evident.

Security architecture risk: ⚪ Minimal · up to eee96

The change adds a fixed client identifier to existing Claude usage requests. It does not change account selection, credentials, destinations, or access permissions. No introduced or worsened security concern was identified.

Retained concerns No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The changed behavior is limited to usage reads for enabled Claude accounts returned by an already configured hub. The fixed identifier adds no caller-controlled destination, account selector, or credential.

Trust Boundaries and Controls

  • observed — The existing two-hop credential structure remains: the management key authenticates the server-to-hub request, while the upstream request body retains the Bearer $TOKEN$ placeholder and selected auth_index. The added User-Agent does not replace either credential mechanism in this source.

Resilience and Maintainability Implications

  • observed — Existing failure containment remains in place: management requests time out after 15 seconds, non-success upstream statuses become errors, and failed account reads produce unavailable usage limits. The new identifier does not add retries or bypass response-status checks.
Pre-merge checks: Passed 4
Check nameStatusExplanation
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.
Title checkPassedThe title clearly and concisely describes the main change: restoring Claude subscription usage through proxies.
Description checkPassedThe description provides the problem, implementation, scope context, focused verification results, limitations, and agent details. The required template sections are covered in prose, although they ar…
Full details: Description check

Explanation

The description provides the problem, implementation, scope context, focused verification results, limitations, and agent details. The required template sections are covered in prose, although they are not explicitly labeled.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR

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

Taking over this one. I’ll reduce the tests to the useful coverage, update the implementation against current main, and verify it before publishing the changes.

Added 4 commits

  • Merge remote-tracking branch 'origin/main' into takeover/12069ee05498
  • Merge remote-tracking branch 'origin/main' into takeover/120694382f69
  • test(server): reuse Claude usage fixture for proxy headereee96ed
  • chore: merge current main for Claude usage proxy fixca30fa5
Synced from GitHub

I checked the production usage module from base 10f7db7 before the proposed ca30fa5 version against the same local management HTTP fixture. Under the fixture’s modeled rule (429 without a Claude Code User-Agent), base produced probeFailed; the proposed version sent claude-code/2.1.273 and mapped the five-hour and seven-day windows. Codex request headers and an independent account’s error isolation were preserved. The focused suite passed 19/19, including the added runtime fixture.

This verifies local request shape and mapping, not real CLIProxyAPI forwarding or Anthropic acceptance. Dependencies were reused from another checkout whose full lockfile differs; exact source hashes and dependency provenance are included. The reported live 429-to-200 result remains independently unverified, and the existing human usage/metering approval gate remains.

Fixture, serialized before/after requests and outputs, raw test logs, and limitations. No live credentials or external quota were used.