sipeed/picoclaw · error

usage request failed (%d): %s

Error message

usage request failed (%d): %s

What it means

The usage request returned a non-200 status other than 403 (anthropic_usage.go:52); the status code and raw response body are embedded in the message. Typical codes: 401 (expired/invalid token), 429 (rate limited), 500/503 (upstream incidents). The body text is the fastest way to tell which.

Source

Thrown at pkg/auth/anthropic_usage.go:52

	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,
		SevenDayUtilization: result.SevenDay.Utilization,
	}, nil

View on GitHub (pinned to 49183d7e8d)

Solutions

  1. Parse the (%d) status from the message: 401 -> refresh/re-login first; 429 -> back off; 5xx -> retry later
  2. For 401, refresh the credential and retry once
  3. For 429, reduce polling frequency and add exponential backoff with jitter
  4. Check status.anthropic.com for incidents when 5xx bodies appear

Example fix

// before: fixed-interval usage polling
for range time.Tick(5 * time.Second) { fetchUsage(token) }

// after: backoff on 429/5xx, refresh on 401, halt on 403
switch {
case strings.Contains(msg, "(401)"): refreshAndRetry()
case strings.Contains(msg, "(429)"): time.Sleep(backoff())
}
Defensive patterns

Strategy: try-catch

Try / catch

usage, err := fetchUsage(ctx, token)
if err != nil && strings.Contains(err.Error(), "usage request failed") {
    msg := err.Error()
    switch {
    case strings.Contains(msg, "(401)"): refreshCredential(); return retryLater
    case strings.Contains(msg, "(429)"), strings.Contains(msg, "(5"): return retryWithBackoff
    default: return err // 4xx other than above: non-retryable
    }
}

Prevention

When it happens

Trigger: GET to the usage endpoint with an expired access token (401); hammering the endpoint beyond its rate limit (429); Anthropic-side incident returning 5xx with an error JSON body; wrong region/auth header producing 400.

Common situations: Long-lived process holding a token past expiry without refresh; tight polling loops (e.g. every few seconds) triggering 429; API incidents; clock skew breaking token validity windows.

Related errors


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