Skip to content

Second Claude account's usage never appears in the Limits tab — silently marked unsupported, never recovers #13053

Description

@jaypopat

What happened

In the pricing tab, if i have 2 claude instances the token limit/usage doesn't show for both of them.

Two Claude provider instances are configured, each a separate Anthropic account, each with its own CLAUDE_CONFIG_DIR (homePath). Only one of the two ever shows a bar in Usage → Limits. The second instance never appears there — no bar, no warning, no error — even though it's actively used and has hit its own real rate limits.

Diagnosis

Traced through apps/server/src/provider/Layers/claudeUsageLimits.ts (claudeUsageResponseToLimits) at commit 719a76ca (tag v0.0.42):

if (!response.rate_limits_available || !response.rate_limits) {
  return { limits: makeUnavailableUsageLimits({ checkedAt, reason: "unsupported" }), ... };
}

For the second instance, the boot-time get_usage SDK probe evidently reports rate_limits_available: false (inferred from client behavior — the raw probe response body isn't logged anywhere, so I could not confirm exactly which condition on that instance triggers it; the instance uses a distinct CLAUDE_CONFIG_DIR and also has a CLAUDE_CODE_OAUTH_TOKEN environment override configured, but the reporter's actual sign-in for that instance was interactive, not token-based, so the trigger is unconfirmed). Whatever the trigger, once it fires, T3 marks the instance "unsupported" — the same bucket used for API-key accounts that genuinely have no subscription. That is silent by design in two places:

  • collectLimitAccounts (packages/shared/src/usageLimits.ts) skips any provider whose usageLimits carries a notice, including "unsupported" — no bar drawn.
  • collectLimitNotices explicitly continues past reason === "unsupported" — no warning shown either.

This traps the instance permanently: applyUsageLimitsUpdate (apps/server/src/provider/providerUsageLimits.ts) refuses to apply later updates once a snapshot is marked "unsupported", on the assumption "an account that cannot have subscription windows will not start reporting them mid-turn." That assumption doesn't hold here — the provider event log shows genuine mid-turn rate_limit_event data from this same instance (five_hour at 109% utilization, seven_day at 45%), proving the account does have real, trackable quota. That data is discarded on arrival because the snapshot is already locked to "unsupported".

Net effect: a second Claude account with real subscription limits is indistinguishable, in this code path, from an account with no subscription at all, and disappears from the Limits tab with zero user-facing feedback — regardless of which auth method it used to sign in.

Checked upstream issue #12170, which covers a related but distinct bug (a transient probeFailed state after a failed boot probe, which self-heals within ~5 minutes via periodic refresh). This case doesn't self-heal — every fresh probe re-derives the same "unsupported" state, so it persists indefinitely across days of use. Not a duplicate.

Steps to reproduce

  1. Configure a second Claude provider instance signed into a different Anthropic account than the first, using its own homePath (separate CLAUDE_CONFIG_DIR).
  2. Open Usage → Limits.
  3. Observe: only one of the two instances ever gets a bar or a notice, before or after refreshing.
  4. Run a turn on the instance with no bar until it hits a rate limit. The mid-turn warning carries real utilization data, but the Limits tab still shows nothing for that instance.

Version

0.0.42 (also confirmed present in the currently-running desktop build, 0.0.43-nightly.20260919.1978 — no relevant commits landed between the two)

Environment

Linux x64, Node v26.8.2, claude CLI 2.1.278

Evidence

[2026-09-20T18:58:27.751Z] CANON: {"type":"runtime.warning","provider":"claudeAgent","payload":{"message":"Claude usage limit reached. This turn is paused until the 5-hour limit resets in 2h 12m.","detail":{"status":"rejected","resetsAt":1789938600,"rateLimitType":"five_hour","overageStatus":"rejected","overageDisabledReason":"org_level_disabled","isUsingOverage":false,"unifiedWindows":{"five_hour":{"utilization":1.09,"resetsAt":1789938600},"seven_day":{"utilization":0.45,"resetsAt":1789938000}}}},"providerInstanceId":"claudeAgent_bagri"}

Related issues

#12170 — related mechanism (probeFailed → sparse window merge), but transient/self-healing, unlike this permanent "unsupported" lock. Not a duplicate.

Fix applied or workaround

None found.

Filed by

claude (sonnet-5) via t3 triage

Activity

  1. juliusmarminge commented on Sep 22, 2026

    @juliusmarminge
    Member

    Triage

    Verdict: Confirmed on v0.0.42 (719a76ca1) and current main (aff9318bf). Keep open. Not fixed by nightlies after 0.0.42 (including the cited 0.0.43-nightly.20260919.1978). Not a duplicate of #12170. No later commit touches claudeUsageResponseToLimits / the unsupported lock in applyUsageLimitsUpdate.

    This is a real server usage-limits bug. A second Claude instance can be permanently classified as unsupported after the boot get_usage probe, then silently omitted from Usage → Limits with no bar and no notice — even while mid-turn rate_limit_event data proves the account has real subscription windows.

    What you reported

    Two Claude provider instances, separate Anthropic accounts, each with its own homePath / CLAUDE_CONFIG_DIR. Only one ever appears under Usage → Limits. The other never gets a bar, warning, or error. Hitting a real rate limit on the missing instance surfaces utilization in a turn warning (five_hour at 109%, seven_day at 45% for claudeAgent_bagri), but Limits still shows nothing for it. Persists across days; refresh does not heal it.

    That matches the code.

    What the code does

    1. Probe → unsupported. probeClaudeCapabilities calls the experimental get_usage API per instance (with that instance’s CLAUDE_CONFIG_DIR from makeClaudeEnvironment). claudeUsageResponseToLimits collapses both of these shapes to unavailable: { reason: "unsupported" }:

      if (!response.rate_limits_available || !response.rate_limits) {
        return { limits: makeUnavailableUsageLimits({ checkedAt, reason: "unsupported" }), ... };
      }

      So rate_limits_available: false (API-key / Bedrock intent) and rate_limits_available: true with rate_limits: null (transient fetch failure — the fix(claude): keep limits when the usage fetch fails #10597 shape) are indistinguishable. The raw probe body is not logged, so this report’s exact trigger is unconfirmed; either path yields the same lock.

    2. Limits UI hides unsupported completely.

      • collectLimitAccounts skips any provider where limitsNotice(...) !== null. For unsupported, limitsNotice returns “This account has no subscription limits.”
      • collectLimitNotices then explicitly continues past reason === "unsupported", so that message never appears on the Limits tab either.
      • Net: no bar, no notice — silent disappearance.
    3. Mid-turn data cannot recover. applyUsageLimitsUpdate returns previous unchanged when previous?.unavailable?.reason === "unsupported", on the assumption “an account that cannot have subscription windows will not start reporting them mid-turn.” The logged rate_limit_event / runtime.warning for claudeAgent_bagri falsifies that for this instance: real five_hour / seven_day utilization is produced and then discarded.

    4. Re-probes re-lock. resolveUsageLimitsAfterProbe treats unsupported as authoritative (it replaces published limits). Every successful probe that re-derives unsupported keeps the instance buried. Unlike Claude usage limits: a rate_limit_event after a failed probe is published as a complete window list #12170’s probeFailed path, this does not self-heal on the 5-minute refresh.

    homePath isolation itself is fine (ClaudeHome.ts / adapter tests). The bug is classification + lock + silent UI skip, not config-dir mixing.

    Related, not duplicates

    Work What it is Why this is separate
    #12170 / open #12700 Sparse rate_limit_event after probeFailed published as a complete one-window list Transient / self-heals; different unavailable reason. This issue is a permanent unsupported lock with no Limits row.
    Closed #10597 Map available: true + rate_limits: null → probeFailed instead of unsupported Would help if that is the second instance’s probe shape. Closed (V2 freeze note); never landed. Does not cover a true available: false on a subscription account. Landing it without #12170/#12700 would also widen the sparse-merge hole.
    Open #12799 Grok: false unsupported → same silent Limits vanish Same UI/contract footgun, different provider mapper. Pattern confirmation, not a Claude fix.

    No open issue tracks “second Claude account stuck as unsupported and invisible on Limits.”

    Suggested fix

    Keep this a small server (+ optional shared UI) bugfix. Do not invent a new limits protocol.

    1. Narrow unsupported in claudeUsageResponseToLimits. Reserve it for accounts that can never report subscription windows (API key / Bedrock / Vertex). Map rate_limits_available: true + missing rate_limits to probeFailed (the fix(claude): keep limits when the usage fetch fails #10597 distinction). If interactive OAuth / Max accounts can still return available: false under a separate CLAUDE_CONFIG_DIR, treat that as probeFailed (or log and investigate) rather than a permanent can-never-report mark.
    2. Do not let a false unsupported discard mid-turn windows forever. Options (pick one, keep it small):
    3. Log the get_usage shape (rate_limits_available, whether rate_limits is null) on the probe path so the next multi-instance report is not inferred.
    4. UI (small, optional): for an authenticated Claude instance marked unsupported, show a notice instead of the total omit in collectLimitNotices. True API-key accounts can stay muted; subscription-looking accounts should not vanish.
    5. Tests: second-instance / available:true+null → not permanent silent omit; unsupported + mid-turn { id: "five_hour" } must not stay a black hole if you choose (2); existing API-key → unsupported case stays green.

    Coordinate with #12700 so a probeFailed reclassification does not reintroduce weekly-only “complete” snapshots.

    Do not treat “refresh Limits,” “wait for provider health,” or landing #10597 alone as the fix.

    Workaround

    None that restores the Limits row while the probe keeps returning the failing shape. Mid-turn rate-limit warnings still carry real utilization for that instance. Ensuring both instances are interactively signed in under their own homePath (and that env overrides like CLAUDE_CODE_OAUTH_TOKEN are not pointed at the wrong account) is worth checking, but this report’s evidence already shows a live subscription on the missing instance.

    Classification: bug · accepted · medium (server usage-limits; multi-Claude-instance; permanent unsupported lock; silent Limits omit; related to #12170 / #10597 / #12799)

  2. added
    acceptedfeature request accepted
    bugSomething is broken or behaving incorrectly.
    via-triageFiled through npx t3 triage
    on Sep 22, 2026
  3. fatalist commented on Oct 3, 2026

    @fatalist

    Hit this with a single Claude instance (no second account, no CLAUDE_CODE_OAUTH_TOKEN override), and I can confirm which get_usage response triggers it.

    Setup: T3 Code (Nightly) 0.0.46-nightly.20261003.2638, Claude Code CLI 2.1.288, Claude Team subscription, macOS 27.2. Usage → Limits only shows Codex. Claude isn't there at all, and there's no notice either.

    What the CLI returns: I ran the same calls the capabilities probe makes (initializationResult() and then usage_EXPERIMENTAL_MAY_CHANGE_DO_NOT_RELY_ON_THIS_API_YET()) through @anthropic-ai/claude-agent-sdk against the same CLI binary:

    {
      "subscription_type": "team",
      "rate_limits_available": true,
      "rate_limits": null
    }

    I got the same result with settingSources: [], so it isn't caused by my user settings.

    So the account clearly has subscription limits (rate_limits_available: true), but rate_limits is null. In the 2.1.288 bundle, rate_limits is only null when the CLI's own usage fetch doesn't come back with status: "ok", e.g. an in-band error, a 429, or an empty body. In other words, the fetch failed on the CLI side. It doesn't mean the account has no limits.

    claudeUsageResponseToLimits treats both cases the same way:

    if (!response.rate_limits_available || !response.rate_limits) {
    return {
    limits: makeUnavailableUsageLimits({ checkedAt, reason: "unsupported" }),
    names: { overageIncluded: undefined },
    };
    }

    and then collectLimitNotices skips "unsupported" without showing anything:

    // An account that can never report (API key) is left out; one that
    // failed, or reported nothing at all, is worth a line.
    if (provider.usageLimits?.unavailable?.reason === "unsupported") continue;

    Suggestion: only use "unsupported" when rate_limits_available === false. When it's true but rate_limits is null, treat it as probeFailed, so the user sees a notice, later rate_limit_events aren't locked out, and the next probe can recover.

    Screenshots: the composer is on Claude Opus 5, and the Limits tab shows only Codex.

    Image Image
  4. caitlon commented on Oct 5, 2026

    @caitlon

    Confirmed one trigger for the silent unsupported: a token from claude setup-token passed through CLAUDE_CODE_OAUTH_TOKEN.

    Same Claude Max account, same machine, same SDK probe T3 runs (initializationResult() + usage_EXPERIMENTAL…()):

    auth account.subscriptionType get_usage
    CLAUDE_CODE_OAUTH_TOKEN from claude setup-token undefined rate_limits_available: false
    /login (credentials file, scopes include user:profile) Claude Max true, windows present

    It shows up as soon as T3 runs on a remote machine over SSH: the macOS keychain is locked in an SSH session, so a setup token is the usual way to sign Claude in there. The Limits tab then says "No provider on the selected environments reports subscription limits", while Tokens and Cost still work, so nothing points at the token.

    Telling apart "no subscription" from "signed in with an inference-only token" would make this self-explanatory: the init result already reports tokenSource: CLAUDE_CODE_OAUTH_TOKEN, so the Limits tab could say that limits need a full claude /login instead of staying silent.

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

    acceptedfeature request acceptedbugSomething is broken or behaving incorrectly.via-triageFiled through npx t3 triage

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions