{"record":{"id":"8bce850a4a745758","repo":"odysseus-dev/odysseus","slug":"github-device-code-request-failed-http-status","errorCode":null,"errorMessage":"GitHub device-code request failed (HTTP {status})","messagePattern":"GitHub device-code request failed \\(HTTP (.+?)\\)","errorType":"http","errorClass":"HTTPException","httpStatus":502,"severity":"error","filePath":"routes/copilot_routes.py","lineNumber":111,"sourceCode":"    # Best-effort: refresh the model cache so the new endpoint shows up.\n    try:\n        from routes.model_routes import _invalidate_models_cache\n        _invalidate_models_cache()\n    except Exception:\n        pass\n    return result\n\n\ndef _start_device_flow(request: Request, form) -> DeviceFlowStart:\n    host = copilot.GITHUB_HOST\n    ent = str(form.get(\"enterprise_url\") or \"\").strip()\n    if ent:\n        host = copilot.normalize_domain(ent)\n    try:\n        data = copilot.request_device_code(host)\n    except httpx.HTTPStatusError as e:\n        status = e.response.status_code if e.response is not None else \"unknown\"\n        raise HTTPException(502, f\"GitHub device-code request failed (HTTP {status})\")\n    except Exception as e:\n        raise HTTPException(502, f\"GitHub device-code request failed: {e}\")\n\n    device_code = data.get(\"device_code\")\n    if not device_code:\n        raise HTTPException(502, \"GitHub did not return a device code\")\n\n    # verification_uri_complete embeds the user code, so the browser tab we\n    # open lands the user straight on GitHub's \"Authorize\" screen with the\n    # code pre-filled — one click, no manual code entry.\n    return DeviceFlowStart(\n        pending={\n            \"device_code\": device_code,\n            \"host\": host,\n            \"enterprise_url\": ent,\n            \"owner\": get_current_user(request) or None,\n        },\n        response={","sourceCodeStart":93,"sourceCodeEnd":129,"githubUrl":"https://github.com/odysseus-dev/odysseus/blob/f9235ebbf13f693a6fd29ce70b097f6ec83705bf/routes/copilot_routes.py#L93-L129","documentation":"Raised (HTTP 502) by the Copilot device-flow login start helper when copilot.request_device_code(host) fails with an httpx.HTTPStatusError — i.e. GitHub's (or GitHub Enterprise Server's) device-code endpoint returned a non-2xx HTTP status. The 502 signals the failure is upstream at the OAuth provider, not in this app; the status code from the failed response (or 'unknown' if the exception carries no response) is embedded in the message. Host selection honors an enterprise_url form field normalized via copilot.normalize_domain.","triggerScenarios":"Starting Copilot device-code login while github.com/login/device/code (or the GHES equivalent at the enterprise domain) returns an error status: 401/403 for a bad or expired OAuth client_id, 404 when the enterprise URL is wrong or the instance does not enable device flow, 429 rate limiting, 5xx outages.","commonSituations":"Misconfigured enterprise_url (points at a GHES host where the device-flow app is not registered); GitHub rate-limits device-code requests during repeated login attempts; GitHub or GHES outage; the app's Copilot OAuth client credentials were revoked.","solutions":["If enterprise_url was entered, verify it is the correct GitHub Enterprise Server domain and that device-flow OAuth is enabled there; otherwise clear it to use github.com.","Wait and retry if the embedded status is 429 (rate limited) — repeated login restarts compound the limit.","If status is 401/403/404 on plain github.com, check the app's Copilot OAuth client configuration (client_id) in the copilot module.","Check https://www.githubstatus.com for 5xx-side outages when the status is 500+."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// Pre-flight: the device endpoint should respond before starting login\nasync function githubReachable(host = 'github.com') {\n  const res = await fetch(`https://${host}/login/device/code`, { method: 'HEAD' }).catch(() => null);\n  return res !== null;\n}","typeGuard":null,"tryCatchPattern":"try { start = await post('/copilot/device/start', form); } catch (e) { if (e.status === 502 && /HTTP (429|5\\d\\d)/.test(e.message)) { await backoff(); return retryStart(); } if (/HTTP 40[13]/.test(e.message)) { showHelp('Check enterprise_url / OAuth client config'); } throw e; }","preventionTips":["Validate enterprise_url format before submitting and let users clear it to fall back to github.com.","Rate-limit login-start attempts client-side (no auto-retry loops) to avoid 429s."],"tags":["copilot","oauth","device-flow","github","upstream-error"],"backgroundTag":null,"analyzedSha":"f9235ebbf13f693a6fd29ce70b097f6ec83705bf","analyzedAt":"2026-08-14T21:47:48.359Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}