decolua/9router · info

[Claude Usage] OAuth endpoint returned

Error message

[Claude Usage] OAuth endpoint returned ${oauthResponse.status}, falling back to legacy

What it means

fetchClaudeUsageRaw first calls Anthropic's OAuth usage endpoint; on any non-ok status (except the handled 429 cooldown path) it logs this warning and transparently falls back to the legacy settings + org usage endpoint. The value returned to the caller comes from the legacy path, so usage data is usually still available.

Solutions

  1. No action needed if the legacy fallback returns data — this is a downgrade warning only
  2. If fallback also fails, re-authenticate the Claude OAuth connection to get a fresh token
  3. Confirm the connected account's plan supports the OAuth usage endpoint
  4. Check Anthropic status/incidents if the status is 5xx
  5. Verify proxy settings aren't altering the usage request

Example fix

// before: stale token keeps hitting the new endpoint with 401
// after: re-run OAuth to refresh credentials, or shorten refresh interval
await refreshToken(connectionId); // before next fetchClaudeUsageRaw call
Defensive patterns

Strategy: fallback

Validate before calling

const conn = await api.get(`/api/connections/${id}`);
if (!conn || conn.tokenExpiresAt < Date.now()) await reauth(id); // refresh before usage fetch

Type guard

const hasUsageData = (u) => u && !u.message && typeof u === 'object';
if (!hasUsageData(usage)) console.warn('usage unavailable:', usage?.message);

Try / catch

try {
  const usage = await fetchClaudeUsageRaw(token);
  // legacy fallback already applied internally; validate shape
  if (usage?.message) console.warn(usage.message);
  return usage;
} catch (e) {
  console.warn(`claude usage unavailable: ${e.message}`);
  return null;
}

Prevention

When it happens

Trigger: The OAuth usage endpoint returned 400/401/403/404/5xx — expired OAuth token, account not eligible for the new usage API, region/plan without the endpoint, or transient upstream 5xx.

Common situations: OAuth access token expired or revoked (401); account on a plan that lacks the OAuth usage endpoint (404); Anthropic API change moved/renamed the endpoint; transient 500/503 during Anthropic incidents.

Related errors


AI-assisted analysis of decolua/9router@90b52e06ff (2026-08-30). Data as JSON: /api/errors/661f3dcd2bad8e44. Report an issue: GitHub.

Appendix: source

Thrown at open-sse/services/usage/claude.js:126

          const modelName = key.replace("seven_day_", "");
          quotas[`weekly ${modelName} (7d)`] = createQuotaObject(value);
        }
      }

      return {
        plan: "Claude Code",
        extraUsage: data.extra_usage ?? null,
        quotas,
      };
    }

    // Cool down OAuth usage polling after a 429 (quota endpoint only)
    if (oauthResponse.status === 429) {
      oauthCooldown.set(accessToken, Date.now() + OAUTH_429_COOLDOWN_MS);
    }

    // Fallback: legacy settings + org usage endpoint
    console.warn(`[Claude Usage] OAuth endpoint returned ${oauthResponse.status}, falling back to legacy`);
    return await getClaudeUsageLegacy(accessToken, proxyOptions);
  } catch (error) {
    return { message: `Claude connected. Unable to fetch usage: ${error.message}` };
  }
}

/**
 * Legacy Claude usage for API key / org admin users
 */
async function getClaudeUsageLegacy(accessToken, proxyOptions = null) {
  try {
    const settingsResponse = await proxyAwareFetch(CLAUDE_CONFIG.settingsUrl, {
      method: "GET",
      headers: {
        "Authorization": `Bearer ${accessToken}`,
        "anthropic-version": CLAUDE_CONFIG.apiVersion,
      },
    }, proxyOptions);

View on GitHub (pinned to 90b52e06ff)