Skip to content

fix(ai): claude quota returns ok:true with empty windows for a 200 payload carrying no usable quota data #1915

Closed

Description

@dezren39

fix(ai): claude quota returns ok:true with empty windows for a 200 payload carrying no usable quota data

Verified 2026-11-06 on origin/main 10f305865f (merge of PR #1732), in a laptop worktree (~/git/fleet/probe-2026-11-06-creator-2235/wt, kept).

The defect

packages/ai/src/quota/providers/claude.ts, provider.fetchQuota (~:265-280): after a 200 response, the result of toClaudeUsage(payload) is published unconditionally:

const { windows, models } = toClaudeUsage(payload)
const usage = Object.keys(models).length > 0 ? { windows, models } : { windows }
return withMetadata(buildResult({ providerId, providerName, ok: true, configured: true, usage, ... }), credential)

There is no empty check. toClaudeUsage's own guard (addWindow: a window needs a percent or a value label) correctly produces no windows for a figure-less payload — and the provider then reports that empty set as a successful quota read: ok: true, configured: true, usage: { windows: {} }, no error.

Claude is the only one of the 13 quota providers that does this. Every sibling implements the no-data contract on the same shape:

Runtime probe (laptop worktree, probe-creator.ts, kept in the worktree)

Driving the real provider.fetchQuota with a stubbed fetch and an env credential:

  • {} → ok: true, configured: true, usage: { windows: {} } — a successful read of nothing.
  • { limits: [] } → identical ok: true with empty windows.
  • { limits: [{ kind: "session", resets_at: "2026-12-01T00:00:00Z" }] } (a limit entry with a reset but no percent — no figure) → identical ok: true with empty windows.
  • Control { five_hour: { utilization: 10, resets_at: "2026-12-01T00:00:00Z" } } → ok: true with the real 5h window at 10% used (correct, unchanged).

Why it matters

ok: true is the signal downstream consumers (the aggregator gatherer, the dashboard, quota-aware placement) act on: an empty-but-successful Claude row reads as "the account was read and has no limits" instead of the honest "the read carried no quota data" — a wire-shape change, a partial Anthropic response, or a new account type silently becomes a healthy-looking empty picture, and the failure is invisible precisely because nothing errors. The sibling fleet settled this exact question in #1762: no usable data is an error row, not a success.

Acceptance criteria

  • A 200 payload from which toClaudeUsage derives no windows and no models returns the sibling no-data contract: ok: false, configured: true, error "No quota data in response" (still carrying the credential metadata via withMetadata).
  • Payloads with any real window (limits, legacy five_hour/seven_day, spend) or any model-scoped window are unchanged, byte-for-byte.
  • packages/ai/test/quota.test.ts pins the provider-level cases: empty object, empty limits, figure-less limit entry, and the real-figure control.
  • Existing quota suite stays green.

Dedup

Searched open + closed issues for "claude quota no-data / empty / ok:true" this run: no covering issue. #1781 (open) is the claude duplicate-limits overwrite — a different defect in the same transform. #1762 (closed) established the no-data contract for codex and named the sibling set; claude was not in its fix scope.

Activity

  1. added
    kind/bugSomething isn't working (prefixed form of the classic bug label)
    task/small-fixQueue packet kind: bounded fix with reproduction and targeted tests
    source/creatorFiled by the new-issue-creator job
    priority/normalOrdinary priority (the default)
    app/aiPackage: ai (pkgs/ or apps/ in this repo)
    work/self-assignedAn agent grabbed this item directly, outside the queue
    effort/xsTrivial: expected under 30 minutes of work
    on Nov 7, 2026
  2. dezren39 commented on Nov 7, 2026

    @dezren39
    Author

    🤖 Agent note · picked up

    • Agent: new-issue-creator-opencode (new-issue-creator job)
    • Session / ID: new-issue-creator-opencode run 2026-11-07T04:35Z (scheduled_for 2026-11-06 22:35 CST)
    • Found via: filed this run
    • Task: Guarded self-solve (§4): claude quota fetchQuota returns the sibling no-data contract (ok:false, "No quota data in response") when a 200 payload yields no windows and no models; add provider-level pins in quota.test.ts.
    • Expectation: effort/xs — one guard in claude.ts plus tests; done = PR merged with quota suite + ai typecheck green on the laptop.
    • Links: branch creator/1915-claude-quota-empty-ok (to come)
  3. added a commit that references this issue on Nov 7, 2026
  4. developing-today commented on Nov 7, 2026

    @developing-today

    🤖 Agent note · done

    • Agent: new-issue-creator-opencode (new-issue-creator job)
    • Session / ID: new-issue-creator-opencode run 2026-11-07T04:35Z (scheduled_for 2026-11-06 22:35 CST)
    • Found via: filed this run
    • Task: Self-solved: PR fix(ai): claude quota returns the no-data contract for a figure-less 200 payload (#1915) #1916 merged (merge commit cfc5e3d) — claude quota fetchQuota now returns the sibling no-data contract (ok:false, "No quota data in response") when a 200 payload yields no windows and no models.
    • Expectation: done — quota suite 124 pass on branch and on post-merge main (negative control: the new no-data test fails with the fix stashed), ai typecheck clean. Transient labels stripped in the same pass.
    • Links: PR fix(ai): claude quota returns the no-data contract for a figure-less 200 payload (#1915) #1916 · probe worktree ~/git/fleet/probe-2026-11-06-creator-2235 (laptop)
    • Correction: the original done-note post in this run was rate-limited on the user token ("submitted too quickly") and an early completion notification falsely reported it as posted; this note is the real done note, posted via the developing.today App and verified by readback.
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

    app/aiPackage: ai (pkgs/ or apps/ in this repo)effort/xsTrivial: expected under 30 minutes of workkind/bugSomething isn't working (prefixed form of the classic bug label)priority/normalOrdinary priority (the default)source/creatorFiled by the new-issue-creator jobtask/small-fixQueue packet kind: bounded fix with reproduction and targeted tests

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions