Skip to content

[Bug]: Thread details Limits shows Unavailable for a Claude provider on a CLIProxyAPI endpoint, though Usage shows the account's limits #30990

Open

Description

@Ahmed-BarqO3

Before submitting

Area

apps/web

Steps to reproduce

  1. Add a Claude provider instance (driver claudeAgent) with environment ANTHROPIC_BASE_URL=http://127.0.0.1:8317 and ANTHROPIC_AUTH_TOKEN=<CLIProxyAPI client key>.
  2. Add a usage limit source kind: "cliproxy" with the same URL and the management key. The hub has exactly one Claude OAuth account.
  3. Open a thread on that provider, then open thread details with "Usage limits" enabled.

Expected behavior

The Limits row shows the 5h and weekly windows of the hub's Claude account, which is the account that actually serves this provider's requests.

Actual behavior

  • Thread details shows Limits: Unavailable.
  • The Usage dialog shows the same account's limits from the CLIProxyAPI source correctly (5h 32%, 7d 5% at the time).
  • UsageLimitSources.readSource succeeds. A manual management api-call to https://api.anthropic.com/api/oauth/usage for that account returns 200 with valid five_hour and seven_day data.

Cause, traced in the shipped 0.1.7 bundle:

  1. The provider's own limits are always unavailable here. A non-api.anthropic.com ANTHROPIC_BASE_URL makes the Claude provider auth { type: "customEndpoint" } with no email. probeClaudeCapabilities gets no usage from Claude Code in env-token mode, so the result is makeUnavailableUsageLimits({ reason: "probeFailed" }).
  2. Source accounts are never tied to a provider instance. The report builder used by thread details, in the web usageLimits module, pushes provider entries with instanceId and cliproxy source accounts (sourceLabel: "CLI Proxy") without instanceId.
  3. Thread details looks up by instanceId only:
    report?.accounts.find(e => e.instanceId === threadInstanceId)?.limits
      ?? providers.find(e => e.instanceId === threadInstanceId)?.usageLimits
    So it always falls back to step 1's unavailable limits.
  4. Email merging can't help. The driver:email key is null because the custom-endpoint provider has no auth.email.

Suggested fix: when the thread's claudeAgent provider has auth.type === "customEndpoint" and its ANTHROPIC_BASE_URL matches an enabled cliproxy source url (after normalising scheme, host, port and trailing slash), use that source's Claude account limits in thread details:

  • Exactly one non-disabled Claude account: use its usageLimits.
  • More than one: keep Unavailable, with a tooltip saying the hub has several accounts and routing is ambiguous. Don't guess.
  • Same idea for codex providers pointed at a hub via OPENAI_BASE_URL.

Not covered by this fix: per-turn rate_limit_event. Claude Code doesn't emit it in env-token mode even when the proxy forwards the anthropic-ratelimit-unified-* headers (CLIProxyAPI requests.passthrough-headers: true; verified forwarded).

Impact

Minor bug or occasional failure

Version or commit

T3 Code desktop 0.1.7

Environment

Windows 11 Pro, Claude Code 2.1.318, CLIProxyAPI 8.0.44 on localhost

Logs or stack traces

Provider event logs: direct claudeAgent threads contain rate_limit_event entries; cliproxyapi-claude threads contain 0.
UsageLimitSources.readSource / publish: exit Success.
Image

Workaround

Read the limits from the Usage dialog (CLIProxyAPI source).

Activity

  1. EhsanulHaqueSiam commented on Nov 4, 2026

    @EhsanulHaqueSiam
    Contributor

    Note

    🤖 Opus 5.5 responding on behalf of EhsanulHaqueSiam

    #27740 fixes this case. In its resolveThreadDetailsLimits (apps/web/src/components/chat/ThreadDetailsUsage.logic.ts), when the instance's own limits carry a notice (here probeFailed), the card falls back to a connected hub if exactly one hub account serves that driver, and the rows say From <hub label>. canServeInstance skips a hub account only when both sides have an email and they differ, so a customEndpoint instance with no email still matches the hub's Claude account. Codex instances take the same path.

    Two differences from the suggested fix:

    • It matches hub accounts by driver, not by comparing ANTHROPIC_BASE_URL with the source URL.
    • With several accounts, an unreadable account, or a hub that fails to read, it keeps Limits: Unavailable rather than guessing.

    For hubs with several accounts, #29978 (built on #27740) uses the account the hub's usage-queue recorded for the thread's session id, opt-in per hub. #30634 takes a different approach to that case.

    I read #27740's code but have not run your exact setup (ANTHROPIC_BASE_URL in the provider instance's environment) against it.

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