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,
}, nilView on GitHub (pinned to 49183d7e8d)
Solutions
- Parse the (%d) status from the message: 401 -> refresh/re-login first; 429 -> back off; 5xx -> retry later
- For 401, refresh the credential and retry once
- For 429, reduce polling frequency and add exponential backoff with jitter
- 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
- Parse the embedded (%d) status before deciding to retry
- Refresh tokens on 401, back off on 429/5xx, halt on other 4xx
- Rate-limit usage polling to avoid 429s in the first place
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
- API error %d: %s
- HTTP %d: %s
- reading usage response: %w
- insufficient scope: usage endpoint requires oauth scope
- API error (status %d): %s
AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15).
Data as JSON: /api/errors/92a89fdbbc6616b0.
Report an issue: GitHub.