sipeed/picoclaw · error
HTTP %d: %s
Error message
HTTP %d: %s
What it means
The provider answered with a non-200 status; the message embeds the numeric code plus up to 512 bytes of the response body. This is the server rejecting the request — auth, path, or quota — not a transport failure, and the body preview names the provider's reason.
Source
Thrown at cmd/picoclaw/internal/model/online.go:52
client := &http.Client{Timeout: 15 * time.Second}
req, err := http.NewRequest(http.MethodGet, url, nil)
if err != nil {
return nil, fmt.Errorf("build request: %w", err)
}
req.Header.Set("Authorization", "Bearer "+apiKey)
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("request failed: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, readErr := io.ReadAll(io.LimitReader(resp.Body, 512))
if readErr != nil {
return nil, fmt.Errorf("read error response: %w", readErr)
}
return nil, fmt.Errorf("HTTP %d: %s", resp.StatusCode, strings.TrimSpace(string(body)))
}
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("read response: %w", err)
}
// {"data": [...]} envelope. Distinguish "envelope shape with empty list"
// from "object without a data key" via Data being non-nil after unmarshal:
// json.Unmarshal sets Data to []modelEntry{} for `{"data":[]}` but leaves
// it as nil when "data" is absent or null.
var envelope modelsAPIResponse
if err := json.Unmarshal(body, &envelope); err == nil && envelope.Data != nil {
return envelope.Data, nil
}
// Bare-array shape, including `[]`.
var arr []modelEntryView on GitHub (pinned to 49183d7e8d)
Solutions
- Map the status: 401/403 -> fix the API key; 404 -> adjust the api base path (add or drop /v1); 429 -> wait or raise limits; 5xx -> retry later.
- Read the embedded body preview — providers usually state the exact problem in it.
- Reproduce with curl -H "Authorization: Bearer $KEY" <baseURL>/models to confirm independently of picoclaw.
- Confirm the key belongs to the same account/endpoint as the api base.
Example fix
// before: api_base = "https://provider.example.com" (server routes /v1/models -> 404) // after: api_base = "https://provider.example.com/v1"
Defensive patterns
Strategy: try-catch
Try / catch
if err != nil && strings.HasPrefix(err.Error(), "HTTP ") {
code, _ := strconv.Atoi(strings.Fields(err.Error())[1])
switch {
case code == 429 || code >= 500:
retryWithBackoff()
case code == 401 || code == 403:
failFast("credential rejected")
case code == 404:
failFast("api base path is wrong")
}
} Prevention
- Smoke-test credentials and the base URL with curl before wiring them into config.
- Treat 429/5xx as retryable and other 4xx as terminal in automation.
- Alert on 401s — they usually mean a rotated key was not updated.
When it happens
Trigger: Any resp.StatusCode != 200 at online.go:52: 401/403 for a bad or revoked key, 404 when the api base path is wrong (e.g. /v1 present or missing versus what the server routes), 429 when rate-limited, 5xx on provider errors.
Common situations: API key revoked or issued for a different provider, api_base set with/without /v1 while the server expects the opposite, free-tier quota exhausted, or pointing the client at an endpoint that does not serve /models at all.
Related errors
- API error %d: %s
- ElevenLabs API error (status %d): %s
- API error (status %d): %s
- reading usage response: %w
- usage request failed (%d): %s
AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15).
Data as JSON: /api/errors/38ad4e9ead223144.
Report an issue: GitHub.