{"record":{"id":"c2189246a3b375da","repo":"headroomlabs-ai/headroom","slug":"github-device-authorization-returned-an-invalid-re","errorCode":null,"errorMessage":"GitHub device authorization returned an invalid response.","messagePattern":"GitHub device authorization returned an invalid response\\.","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"headroom/copilot_auth.py","lineNumber":542,"sourceCode":"\n    urls = _github_oauth_urls(domain)\n    body = urlencode({\"client_id\": COPILOT_CHAT_OAUTH_CLIENT_ID, \"scope\": \"read:user\"}).encode(\n        \"utf-8\"\n    )\n    request = urllib_request.Request(\n        urls[\"device_code\"],\n        data=body,\n        headers={\n            \"Accept\": \"application/json\",\n            \"Content-Type\": \"application/x-www-form-urlencoded\",\n            \"User-Agent\": _DEFAULT_USER_AGENT,\n        },\n        method=\"POST\",\n    )\n    with urllib_request.urlopen(request, timeout=timeout) as response:\n        payload = json.loads(response.read().decode(\"utf-8\", errors=\"replace\"))\n    if not isinstance(payload, dict):\n        raise RuntimeError(\"GitHub device authorization returned an invalid response.\")\n    return payload\n\n\ndef poll_copilot_device_authorization(\n    device_code: str,\n    *,\n    domain: str = DEFAULT_GITHUB_HOST,\n    interval: int = 5,\n    expires_in: int = 900,\n    timeout: float = 10.0,\n) -> str:\n    \"\"\"Poll GitHub until the device-code OAuth flow returns an access token.\"\"\"\n\n    urls = _github_oauth_urls(domain)\n    deadline = time.time() + max(1, expires_in)\n    poll_interval = max(1, interval)\n    while time.time() < deadline:\n        body = urlencode(","sourceCodeStart":524,"sourceCodeEnd":560,"githubUrl":"https://github.com/headroomlabs-ai/headroom/blob/322425c43bffde1ed0b64fecf3cf5951565dd82b/headroom/copilot_auth.py#L524-L560","documentation":"Raised in the device-authorization request step of GitHub Copilot OAuth: the HTTP call to the device_code endpoint succeeded (status 200) but the JSON-decoded body is not a dict. GitHub's device flow always returns a JSON object (device_code, user_code, verification_uri, interval), so a non-dict payload means an unexpected response — a proxy, captive portal, or HTML error page that still parsed as JSON (e.g. a bare list or string). It is a RuntimeError, chained over json.loads output.","triggerScenarios":"start_copilot_device_authorization() against https://github.com/login/device/code (or a GHE host) where a corporate proxy or misconfigured GITHUB_HOST returns a JSON scalar/array instead of the expected object.","commonSituations":"Corporate proxies that intercept HTTPS and return their own JSON; pointing domain at a GitHub Enterprise Server version whose endpoints differ; DNS hijack/captive portal responses; a typo in DEFAULT_GITHUB_HOST producing a 200 from a wildcard server.","solutions":["Check DEFAULT_GITHUB_HOST / the domain argument points at a real GitHub or GHE instance (e.g. github.com)","Bypass or configure the intercepting proxy (HTTPS_PROXY / NO_PROXY) so github.com is reached directly","Reproduce with curl: `curl -s -X POST https://github.com/login/device/code -d 'client_id=...' -H 'Accept: application/json' | jq type` — anything other than 'object' confirms interception","If on GHE, verify the device-flow endpoints exist on your server version (device flow needs GHE 3.x+)"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":"def is_device_auth_payload(payload: object) -> bool:\n    return (\n        isinstance(payload, dict)\n        and isinstance(payload.get(\"device_code\"), str)\n        and isinstance(payload.get(\"user_code\"), str)\n    )","tryCatchPattern":"try:\n    auth = start_copilot_device_authorization(...)\nexcept RuntimeError as e:\n    if \"invalid response\" in str(e):\n        # proxy/interception or wrong GHE host — inspect network path, then restart flow\n        raise SystemExit(f\"Device auth response malformed: {e}\") from e\n    raise","preventionTips":["Verify DEFAULT_GITHUB_HOST / domain targets a real GitHub or GHE instance before starting the flow","Pin proxies with HTTPS_PROXY/NO_PROXY so github.com is not intercepted","Smoke-test the device endpoint with curl -H 'Accept: application/json' in new environments"],"tags":["network","auth","oauth","github","device-flow","proxy"],"backgroundTag":null,"analyzedSha":"322425c43bffde1ed0b64fecf3cf5951565dd82b","analyzedAt":"2026-08-15T01:03:05.481Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}