{"record":{"id":"2d5c68c686d93fe5","repo":"headroomlabs-ai/headroom","slug":"no-github-copilot-oauth-token-is-available","errorCode":null,"errorMessage":"No GitHub Copilot OAuth token is available.","messagePattern":"No GitHub Copilot OAuth token is available\\.","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"headroom/copilot_auth.py","lineNumber":1166,"sourceCode":"\n            if explicit_api_token and refresh_oauth_token:\n                if cached is None:\n                    seeded_expires_at = _parse_expiry(os.environ.get(_API_TOKEN_EXPIRES_AT_ENV_VAR))\n                    seeded = CopilotAPIToken(\n                        token=explicit_api_token,\n                        expires_at=seeded_expires_at if seeded_expires_at is not None else 0.0,\n                        api_url=_configured_api_url(),\n                    )\n                    self._cached = seeded\n                    if seeded.is_valid:\n                        return seeded\n                exchanged = await self._exchange_token(refresh_oauth_token)\n                self._cached = exchanged\n                return exchanged\n\n            oauth_token = read_cached_oauth_token()\n            if not oauth_token:\n                raise RuntimeError(\"No GitHub Copilot OAuth token is available.\")\n\n            if not _should_exchange_oauth_token():\n                direct_token = CopilotAPIToken(\n                    token=oauth_token,\n                    expires_at=time.time() + 3600,\n                    api_url=_configured_api_url(),\n                )\n                self._cached = direct_token\n                return direct_token\n\n            exchanged = await self._exchange_token(oauth_token)\n            self._cached = exchanged\n            return exchanged\n\n    async def _exchange_token(self, oauth_token: str) -> CopilotAPIToken:\n        headers = _copilot_token_exchange_headers(oauth_token)\n        payload = await asyncio.to_thread(self._exchange_token_sync, headers)\n        token = str(payload.get(\"token\") or \"\").strip()","sourceCodeStart":1148,"sourceCodeEnd":1184,"githubUrl":"https://github.com/headroomlabs-ai/headroom/blob/322425c43bffde1ed0b64fecf3cf5951565dd82b/headroom/copilot_auth.py#L1148-L1184","documentation":"Raised inside CopilotTokenProvider when both credential sources are exhausted: no seeded refresh token was valid, and read_cached_oauth_token() returned None (no cached OAuth entry, or the cached entry was filtered out — e.g. by the _entry_expired check in _extract_oauth_token). The provider then cannot mint a Copilot API token at all, so it raises RuntimeError before attempting the token exchange.","triggerScenarios":"Calling the token provider (get_copilot_token_provider() / its token fetch) on a machine where `headroom` Copilot login never ran, where the cached auth file was deleted, or where the cached entry expired so _extract_oauth_token returned None.","commonSituations":"Fresh machine/CI container without prior device login; auth cache cleared by cleanup scripts or `unwrap`-adjacent tooling; entry's created_at/expiry making _entry_expired true; long gaps between runs exceeding the cached token's lifetime.","solutions":["Run the headroom Copilot device login flow (which calls save_headroom_copilot_oauth_token) to populate the cache","Check the auth file at headroom_copilot_auth_path() exists and its entry is not expired; re-login if it is","In automation, pre-provision the auth file or fall back to an env-provided token instead of assuming a cache exists"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"from headroom.copilot_auth import read_cached_oauth_token\n\nif not read_cached_oauth_token():\n    raise SystemExit(\n        \"No Copilot credentials cached — run `headroom` Copilot login (device flow) first\"\n    )","typeGuard":null,"tryCatchPattern":"try:\n    api_token = await provider.get_token()\nexcept RuntimeError as e:\n    if \"No GitHub Copilot OAuth token\" in str(e):\n        trigger_device_login()  # then retry once\n    else:\n        raise","preventionTips":["Run the device login once per machine and keep the auth cache directory out of cleanup scripts","Pre-check read_cached_oauth_token() before long jobs that will need Copilot tokens","In CI, provision the auth file (or token env) as a setup step rather than assuming a cache"],"tags":["auth","copilot","oauth","cache","credentials"],"backgroundTag":null,"analyzedSha":"322425c43bffde1ed0b64fecf3cf5951565dd82b","analyzedAt":"2026-08-15T01:03:05.481Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}