paperclipai/paperclip · error · Error

anthropic usage api returned ${resp.status}

Error message

anthropic usage api returned ${resp.status}

What it means

Thrown by fetchClaudeQuota when the Anthropic OAuth usage endpoint (https://api.anthropic.com/api/oauth/usage) returns a non-OK HTTP status. The bearer token was sent; the server responded, but not with 200, so the body is not parsed as a quota payload and the raw status code is surfaced for diagnosis.

Source

Thrown at packages/adapters/claude-local/src/server/quota.ts:219

/** fetch with an abort-based timeout so a hanging provider api doesn't block the response indefinitely */
export async function fetchWithTimeout(url: string, init: RequestInit, ms = 8000): Promise<Response> {
  const controller = new AbortController();
  const timer = setTimeout(() => controller.abort(), ms);
  try {
    return await fetch(url, { ...init, signal: controller.signal });
  } finally {
    clearTimeout(timer);
  }
}

export async function fetchClaudeQuota(token: string): Promise<QuotaWindow[]> {
  const resp = await fetchWithTimeout("https://api.anthropic.com/api/oauth/usage", {
    headers: {
      Authorization: `Bearer ${token}`,
      "anthropic-beta": "oauth-2025-04-20",
    },
  });
  if (!resp.ok) throw new Error(`anthropic usage api returned ${resp.status}`);
  const body = (await resp.json()) as AnthropicUsageResponse;
  const windows: QuotaWindow[] = [];

  if (body.five_hour != null) {
    windows.push({
      label: "Current session",
      usedPercent: toPercent(body.five_hour.utilization),
      resetsAt: body.five_hour.resets_at ?? null,
      valueLabel: null,
      detail: null,
    });
  }
  if (body.seven_day != null) {
    windows.push({
      label: "Current week (all models)",
      usedPercent: toPercent(body.seven_day.utilization),
      resetsAt: body.seven_day.resets_at ?? null,
      valueLabel: null,

View on GitHub (pinned to 67001ec6eb)

Solutions

  1. Refresh the Claude OAuth credentials: run `claude login` so readClaudeToken returns a fresh access token.
  2. Map the status code: 401/403 -> re-auth; 429 -> retry with backoff; 5xx -> transient, retry later.
  3. Confirm the token has the expected OAuth scopes for the usage endpoint.
  4. If using the quota-probe CLI, pass --cli-only to bypass the OAuth path and use the CLI-based quota source instead.

Example fix

// before
const windows = await fetchClaudeQuota(staleToken);
// after
// run `claude login` to refresh, then:
const token = await readClaudeToken();
const windows = await fetchClaudeQuota(token);
Defensive patterns

Strategy: try-catch

Try / catch

try {
  const windows = await fetchClaudeQuota(token);
} catch (err) {
  const m = err.message.match(/returned (\d+)/);
  if (m) {
    const status = Number(m[1]);
    if (status === 401 || status === 403) { /* re-auth */ }
    if (status === 429) { /* back off and retry */ }
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling fetchClaudeQuota(token) where the fetch resolves but resp.ok is false. Common status codes: 401 (token expired/revoked), 403 (scope/permission), 429 (rate limited), 5xx (upstream outage), or 404 if the endpoint path changed.

Common situations: Claude OAuth token past its refresh window; an organization whose plan disables the usage endpoint; transient Anthropic API outage; running in a region that gets rate-limited; token from a different Anthropic account tier.

Related errors


AI-assisted analysis of paperclipai/paperclip@67001ec6eb (2026-08-12). Data as JSON: /api/errors/51581df2b8f0958f. Report an issue: GitHub.