headroomlabs-ai/headroom · error · RuntimeError
GitHub device authorization returned an invalid response.
Error message
GitHub device authorization returned an invalid response.
What it means
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.
Source
Thrown at headroom/copilot_auth.py:542
urls = _github_oauth_urls(domain)
body = urlencode({"client_id": COPILOT_CHAT_OAUTH_CLIENT_ID, "scope": "read:user"}).encode(
"utf-8"
)
request = urllib_request.Request(
urls["device_code"],
data=body,
headers={
"Accept": "application/json",
"Content-Type": "application/x-www-form-urlencoded",
"User-Agent": _DEFAULT_USER_AGENT,
},
method="POST",
)
with urllib_request.urlopen(request, timeout=timeout) as response:
payload = json.loads(response.read().decode("utf-8", errors="replace"))
if not isinstance(payload, dict):
raise RuntimeError("GitHub device authorization returned an invalid response.")
return payload
def poll_copilot_device_authorization(
device_code: str,
*,
domain: str = DEFAULT_GITHUB_HOST,
interval: int = 5,
expires_in: int = 900,
timeout: float = 10.0,
) -> str:
"""Poll GitHub until the device-code OAuth flow returns an access token."""
urls = _github_oauth_urls(domain)
deadline = time.time() + max(1, expires_in)
poll_interval = max(1, interval)
while time.time() < deadline:
body = urlencode(View on GitHub (pinned to 322425c43b)
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+)
Defensive patterns
Strategy: try-catch
Type guard
def is_device_auth_payload(payload: object) -> bool:
return (
isinstance(payload, dict)
and isinstance(payload.get("device_code"), str)
and isinstance(payload.get("user_code"), str)
) Try / catch
try:
auth = start_copilot_device_authorization(...)
except RuntimeError as e:
if "invalid response" in str(e):
# proxy/interception or wrong GHE host — inspect network path, then restart flow
raise SystemExit(f"Device auth response malformed: {e}") from e
raise Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- GitHub device authorization expired.
- GitHub device authorization failed: {description}
- failed to download {final_url} after {attempts} attempts: {e
- Copilot OAuth token must not be empty.
- No GitHub Copilot OAuth token is available.
AI-assisted analysis of headroomlabs-ai/headroom@322425c43b (2026-08-15).
Data as JSON: /api/errors/c2189246a3b375da.
Report an issue: GitHub.