sipeed/picoclaw · error

insufficient scope: usage endpoint requires oauth scope

Error message

insufficient scope: usage endpoint requires oauth scope

What it means

The Anthropic usage endpoint answered HTTP 403 (anthropic_usage.go:50): the request authenticated, but the credential's OAuth token lacks the scope required by the usage endpoint. This is a deliberate authorization failure, distinct from a network or parse error, and is non-retryable — retrying the same token always yields 403.

Source

Thrown at pkg/auth/anthropic_usage.go:50

	req.Header.Set("Authorization", "Bearer "+token)
	req.Header.Set("Anthropic-Version", anthropicAPIVersion)
	req.Header.Set("Anthropic-Beta", anthropicBetaHeader)

	client := &http.Client{Timeout: 10 * time.Second}
	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("reading usage response: %w", err)
	}

	if resp.StatusCode != http.StatusOK {
		if resp.StatusCode == http.StatusForbidden {
			return nil, fmt.Errorf("insufficient scope: usage endpoint requires oauth scope")
		}
		return nil, fmt.Errorf("usage request failed (%d): %s", resp.StatusCode, string(body))
	}

	var result struct {
		FiveHour struct {
			Utilization float64 `json:"utilization"`
		} `json:"five_hour"`
		SevenDay struct {
			Utilization float64 `json:"utilization"`
		} `json:"seven_day"`
	}
	if err := json.Unmarshal(body, &result); err != nil {
		return nil, fmt.Errorf("parsing usage response: %w", err)
	}

	return &AnthropicUsage{
		FiveHourUtilization: result.FiveHour.Utilization,

View on GitHub (pinned to 49183d7e8d)

Solutions

  1. Re-authenticate through the current login flow so a fresh token with the usage scope is issued
  2. Verify in the provider console that your OAuth client requests/is granted the usage scope
  3. If your plan/credential type does not include usage scope, disable usage polling rather than retrying
  4. Do not retry in a loop — classify 403 as permanent and surface it to the user

Example fix

// before: poll usage forever with an unscoped token
for { usage, err := fetchUsage(token); if err != nil { continue } }

// after: stop permanently on scope errors
if strings.Contains(err.Error(), "insufficient scope") {
    log.Print("token lacks usage scope; re-login required")
    return err
}
Defensive patterns

Strategy: try-catch

Try / catch

usage, err := fetchUsage(ctx, token)
if err != nil {
    if strings.Contains(err.Error(), "insufficient scope") {
        // permanent: stop polling, prompt re-login
        disableUsagePolling()
        promptRelogin()
        return err
    }
    return err
}

Prevention

When it happens

Trigger: Calling the usage fetch with an OAuth credential minted by a login flow that did not request the usage scope; using a token from an OAuth client/app not allow-listed for usage reporting; org policy stripping the scope from issued tokens.

Common situations: Token from an older login (pre-usage-scope); custom OAuth client_id missing the usage scope; Claude Max/Pro subscription vs API-key confusion where usage endpoint is OAuth-only.

Related errors


AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15). Data as JSON: /api/errors/1815ad53a71fa158. Report an issue: GitHub.