sipeed/picoclaw · error
device code request failed: %s
Error message
device code request failed: %s
What it means
The device-code endpoint answered a non-200 status (oauth.go:271); the raw body is embedded in the error string. Unlike 357 (no response at all), here the server spoke and refused: 404 when cfg.Issuer does not host the /api/accounts/deviceauth/usercode path, 400/422 for an unknown/invalid client_id, 429 for rate limiting, 5xx for upstream faults.
Source
Thrown at pkg/auth/oauth.go:271
"client_id": cfg.ClientID,
})
resp, err := http.Post(
cfg.Issuer+"/api/accounts/deviceauth/usercode",
"application/json",
strings.NewReader(string(reqBody)),
)
if err != nil {
return nil, fmt.Errorf("requesting device code: %w", err)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("reading device code response: %w", err)
}
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("device code request failed: %s", string(body))
}
deviceResp, err := parseDeviceCodeResponse(body)
if err != nil {
return nil, fmt.Errorf("parsing device code response: %w", err)
}
if deviceResp.Interval < 1 {
deviceResp.Interval = 5
}
return &DeviceCodeInfo{
DeviceAuthID: deviceResp.DeviceAuthID,
UserCode: deviceResp.UserCode,
VerifyURL: cfg.Issuer + "/codex/device",
Interval: deviceResp.Interval,
}, nil
}View on GitHub (pinned to 49183d7e8d)
Solutions
- Read the embedded body — it states the server's reason verbatim
- 404: fix cfg.Issuer to the host that actually serves /api/accounts/deviceauth/usercode
- 4xx client errors: verify cfg.ClientID belongs to this issuer environment
- 429/5xx: back off and retry later; do not loop tightly
Example fix
# before: issuer/path mismatch returns 404 cfg.Issuer = "https://oauth2.googleapis.com" # after: issuer that implements the picoclaw device-auth endpoint cfg.Issuer = "https://accounts.pico.ltd"
Defensive patterns
Strategy: try-catch
Try / catch
if _, err := auth.RequestDeviceCode(cfg); err != nil {
if strings.Contains(err.Error(), "device code request failed") {
body := err.Error()
switch {
case strings.Contains(body, "404"): fixIssuer(cfg) // wrong issuer/path
case strings.Contains(body, "400"), strings.Contains(body, "422"): fixClientID(cfg)
case strings.Contains(body, "429"): return retryWithBackoff
default: return err
}
}
return err
} Prevention
- Point cfg.Issuer only at providers implementing the picoclaw device-auth path
- Keep cfg.ClientID consistent with the issuer environment
- Never call RequestDeviceCode in a tight loop; the flow polls tokens per Interval instead
When it happens
Trigger: cfg.Issuer pointed at a standard OAuth issuer whose device path differs (404 HTML body); cfg.ClientID from a different environment (invalid client); hammering the endpoint on every poll loop instead of honoring deviceResp.Interval; provider incidents.
Common situations: Copy-paste of Google/Anthropic issuer values into a picoclaw device flow; staging client_id used against production; polling loops that call RequestDeviceCode repeatedly instead of the token-poll endpoint.
Related errors
- requesting device code: %w
- Invalid device code response
- Failed to load config
- Failed to fetch config
- Failed to save config
AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15).
Data as JSON: /api/errors/cc0d73b7c5f15e38.
Report an issue: GitHub.