{"record":{"id":"56d5c3dccddf6467","repo":"charmbracelet/crush","slug":"device-code-request-failed-s-s","errorCode":null,"errorMessage":"device code request failed: %s - %s","messagePattern":"device code request failed: (.+?) - (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/oauth/copilot/oauth.go","lineNumber":58,"sourceCode":"\n\treq, err := http.NewRequestWithContext(ctx, \"POST\", deviceCodeURL, strings.NewReader(data.Encode()))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treq.Header.Set(\"Accept\", \"application/json\")\n\treq.Header.Set(\"Content-Type\", \"application/x-www-form-urlencoded\")\n\treq.Header.Set(\"User-Agent\", userAgent)\n\n\tclient := &http.Client{Timeout: 30 * time.Second}\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer resp.Body.Close()\n\n\tif resp.StatusCode != http.StatusOK {\n\t\tbody, _ := io.ReadAll(resp.Body)\n\t\treturn nil, fmt.Errorf(\"device code request failed: %s - %s\", resp.Status, string(body))\n\t}\n\n\tvar dc DeviceCode\n\tif err := json.NewDecoder(resp.Body).Decode(&dc); err != nil {\n\t\treturn nil, err\n\t}\n\treturn &dc, nil\n}\n\n// PollForToken polls GitHub for the access token after user authorization.\nfunc PollForToken(ctx context.Context, dc *DeviceCode) (*oauth.Token, error) {\n\tinterval := max(dc.Interval, 5)\n\tdeadline := time.Now().Add(time.Duration(dc.ExpiresIn) * time.Second)\n\tticker := time.NewTicker(time.Duration(interval) * time.Second)\n\tdefer ticker.Stop()\n\n\tfor time.Now().Before(deadline) {\n\t\tselect {","sourceCodeStart":40,"sourceCodeEnd":76,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/oauth/copilot/oauth.go#L40-L76","documentation":"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.","triggerScenarios":"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.","commonSituations":"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).","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"],"exampleFix":"// before\ndc, err := RequestDeviceCode(ctx)\n// after\ndc, err := RequestDeviceCode(ctx)\nif err != nil {\n    if strings.Contains(err.Error(), \"50\") {\n        time.Sleep(2 * time.Second) // retry transient 5xx\n        dc, err = RequestDeviceCode(ctx)\n    }\n    if err != nil { return err }\n}","handlingStrategy":"retry","validationCode":"func checkGitHubReachable() error {\n    resp, err := http.Get(\"https://github.com\")\n    if err != nil { return err }\n    defer resp.Body.Close()\n    if resp.StatusCode >= 500 { return fmt.Errorf(\"github degraded: %s\", resp.Status) }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"dc, err := RequestDeviceCode(ctx)\nif err != nil {\n    if strings.Contains(err.Error(), \"device code request failed\") {\n        var reterr error\n        for i := 0; i < 3; i++ {\n            time.Sleep(time.Duration(1<<i) * time.Second)\n            dc, reterr = RequestDeviceCode(ctx)\n            if reterr == nil { break }\n        }\n        err = reterr\n    }\n}","preventionTips":["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"],"tags":["oauth","device-flow","http","github","copilot"],"backgroundTag":"device-code-request-failed","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}