charmbracelet/crush · error
device code request failed: %s - %s
Error message
device code request failed: %s - %s
What it means
RequestDeviceCode calls GitHub's OAuth device-code endpoint for Copilot and expects HTTP 200. Any other status aborts with "device code request failed: <status> - <body>" so the caller can see both the HTTP status and the server's error payload. This is a network/API-level rejection before any token polling begins.
Source
Thrown at internal/oauth/copilot/oauth.go:58
req, err := http.NewRequestWithContext(ctx, "POST", deviceCodeURL, strings.NewReader(data.Encode()))
if err != nil {
return nil, err
}
req.Header.Set("Accept", "application/json")
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req.Header.Set("User-Agent", userAgent)
client := &http.Client{Timeout: 30 * time.Second}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("device code request failed: %s - %s", resp.Status, string(body))
}
var dc DeviceCode
if err := json.NewDecoder(resp.Body).Decode(&dc); err != nil {
return nil, err
}
return &dc, nil
}
// PollForToken polls GitHub for the access token after user authorization.
func PollForToken(ctx context.Context, dc *DeviceCode) (*oauth.Token, error) {
interval := max(dc.Interval, 5)
deadline := time.Now().Add(time.Duration(dc.ExpiresIn) * time.Second)
ticker := time.NewTicker(time.Duration(interval) * time.Second)
defer ticker.Stop()
for time.Now().Before(deadline) {
select {View on GitHub (pinned to 7944b8e522)
Solutions
- Check network/proxy and retry; verify https://github.com is reachable (curl the endpoint)
- Inspect the response body in the error for GitHub's specific message (e.g. incorrect client_id)
- Check GitHub status page for outages and retry with backoff on 5xx
- Update Crush / the oauth client if GitHub changed the device-flow endpoint
Example fix
// before
dc, err := RequestDeviceCode(ctx)
// after
dc, err := RequestDeviceCode(ctx)
if err != nil {
if strings.Contains(err.Error(), "50") {
time.Sleep(2 * time.Second) // retry transient 5xx
dc, err = RequestDeviceCode(ctx)
}
if err != nil { return err }
} Defensive patterns
Strategy: retry
Validate before calling
func checkGitHubReachable() error {
resp, err := http.Get("https://github.com")
if err != nil { return err }
defer resp.Body.Close()
if resp.StatusCode >= 500 { return fmt.Errorf("github degraded: %s", resp.Status) }
return nil
} Try / catch
dc, err := RequestDeviceCode(ctx)
if err != nil {
if strings.Contains(err.Error(), "device code request failed") {
var reterr error
for i := 0; i < 3; i++ {
time.Sleep(time.Duration(1<<i) * time.Second)
dc, reterr = RequestDeviceCode(ctx)
if reterr == nil { break }
}
err = reterr
}
} Prevention
- Check proxy env vars (HTTP_PROXY/HTTPS_PROXY) before login flows
- Retry 5xx with exponential backoff; do not retry 4xx
- Surface the response body in the error to users for diagnosis
- Pin/update the OAuth client_id when GitHub changes the flow
When it happens
Trigger: The POST to the GitHub device-code endpoint returns a non-200 status: 4xx (bad/missing client_id, malformed request) or 5xx (GitHub outage), or an intermediate proxy returns an error page.
Common situations: Corporate proxy/VPN intercepting requests and returning 403/502; GitHub rate limiting or incident; outdated built-in client_id after a GitHub API change; no network access (though that usually errors earlier at the transport).
Related errors
- copilot token request failed: %s - %s
- authorization timed out
- authorization failed: %s
- create request: %w
- ${ErrorDescription}
AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29).
Data as JSON: /api/errors/56d5c3dccddf6467.
Report an issue: GitHub.