headroomlabs-ai/headroom · error · RuntimeError
GitHub device authorization failed: {description}
Error message
GitHub device authorization failed: {description} What it means
Generic failure branch of the device-flow token poll: GitHub returned a JSON dict with a non-empty 'error' that is neither authorization_pending, slow_down, nor expired_token. The error_description (falling back to the error code) is embedded in the RuntimeError. Typical underlying codes: incorrect_client_credentials, incorrect_device_code, access_denied (user clicked deny), unsupported_grant_type.
Source
Thrown at headroom/copilot_auth.py:598
raise RuntimeError("GitHub device authorization returned an invalid response.")
access_token = payload.get("access_token")
if isinstance(access_token, str) and access_token.strip():
return access_token.strip()
error = str(payload.get("error") or "").strip()
if error == "authorization_pending":
time.sleep(poll_interval)
continue
if error == "slow_down":
poll_interval += 5
time.sleep(poll_interval)
continue
if error == "expired_token":
raise RuntimeError("GitHub device authorization expired.")
if error:
description = str(payload.get("error_description") or error).strip()
raise RuntimeError(f"GitHub device authorization failed: {description}")
time.sleep(poll_interval)
raise RuntimeError("GitHub device authorization expired.")
def _extract_oauth_token(entry: dict[str, Any]) -> str | None:
if _entry_expired(entry):
return None
for key in _OAUTH_TOKEN_KEYS:
value = entry.get(key)
if isinstance(value, str) and value.strip():
return value.strip()
for value in entry.values():
if isinstance(value, dict):
nested = _extract_oauth_token(value)View on GitHub (pinned to 322425c43b)
Solutions
- Read the embedded description: access_denied means the user denied consent — re-run and approve; incorrect_client_credentials means the client_id is wrong — check the configured GitHub App / client ID
- Start a brand-new device flow (fresh device_code) rather than reusing one
- On GitHub Enterprise, have an admin confirm the OAuth App permits the device flow grant
- Handle this as a non-retryable auth failure — do not loop, surface the message to the user
Defensive patterns
Strategy: try-catch
Try / catch
try:
token = poll_copilot_device_authorization(device_code, ...)
except RuntimeError as e:
msg = str(e)
if "access_denied" in msg:
raise SystemExit("User denied Copilot authorization — re-run and approve") from e
if "incorrect_client_credentials" in msg:
raise SystemExit("Client ID invalid — check the GitHub App configuration") from e
raise # other errors: do not retry blindly Prevention
- Treat embedded error_description as terminal (deny/bad credentials) — never auto-retry the same device_code
- Keep the client_id used for device_code and token polls identical
- Alert users that clicking Cancel on GitHub's consent page produces this failure
When it happens
Trigger: The user denies the consent page (access_denied); the client_id sent to the device_code endpoint is invalid or revoked (incorrect_client_credentials); the device_code is malformed/reused from a previous flow; device flow disabled for the GitHub App.
Common situations: Users pressing 'Cancel' on the GitHub authorize page; hardcoded/rotated client IDs in forks of the tool; GHE admins disabling OAuth device flow; retrying a device_code from an old run after it was consumed.
Related errors
- GitHub device authorization returned an invalid response.
- GitHub device authorization expired.
- Copilot OAuth token must not be empty.
- No GitHub Copilot OAuth token is available.
- bedrock_sigv4_failed
AI-assisted analysis of headroomlabs-ai/headroom@322425c43b (2026-08-15).
Data as JSON: /api/errors/2d762d989fd45c22.
Report an issue: GitHub.