HKUDS/DeepTutor · error · RuntimeError

GitHub Copilot token exchange returned no token.

Error message

GitHub Copilot token exchange returned no token.

What it means

After POSTing to the Copilot token-exchange endpoint, the JSON payload lacked a 'token' field, so the provider cannot authenticate subsequent API calls and raises RuntimeError.

Source

Thrown at deeptutor/services/llm/provider_core/github_copilot_provider.py:88

        async with httpx.AsyncClient(
            timeout=timeout, follow_redirects=True, trust_env=True
        ) as client:
            response = await client.get(
                DEFAULT_COPILOT_TOKEN_URL,
                headers={
                    "Authorization": f"token {github_token}",
                    "Accept": "application/json",
                    "User-Agent": USER_AGENT,
                    "Editor-Version": EDITOR_VERSION,
                    "Editor-Plugin-Version": EDITOR_PLUGIN_VERSION,
                },
            )
            response.raise_for_status()
            payload = response.json()

        token = payload.get("token")
        if not token:
            raise RuntimeError("GitHub Copilot token exchange returned no token.")
        expires_at = payload.get("expires_at")
        if isinstance(expires_at, (int, float)):
            self._copilot_expires_at = float(expires_at)
        else:
            refresh_in = payload.get("refresh_in") or 1500
            self._copilot_expires_at = time.time() + int(refresh_in)
        self._copilot_access_token = str(token)
        return self._copilot_access_token

    async def _ensure_api_key(self) -> None:
        now = time.time()
        if self._copilot_access_token and now < self._copilot_expires_at - _EXPIRY_SKEW_SECONDS:
            self.api_key = self._copilot_access_token
            self._client.api_key = self._copilot_access_token
            return

        await self._try_existing_local_auth()

View on GitHub (pinned to 3e82f13042)

Solutions

  1. Log in again (`deeptutor provider login github-copilot`) to refresh the underlying GitHub token.
  2. Check network/proxy isn't intercepting the exchange request.
  3. Update DeepTutor — the exchange contract may have changed.
Defensive patterns

Strategy: retry

Try / catch

try:
    resp = await provider.chat(messages)
except RuntimeError as e:
    if 'token exchange' in str(e):
        relogin_and_retry_once()
    else:
        raise

Prevention

When it happens

Trigger: The exchange endpoint returns 200 but a body without 'token' (or token=null/empty) — API contract change, rate limiting response, or the GitHub token being invalid such that the exchange returns an error-shaped payload with 200.

Common situations: GitHub/Copilot API changes; expired or revoked GitHub OAuth token producing a degenerate response; proxy or firewall returning an HTML interception page parsed as JSON.

Related errors


AI-assisted analysis of HKUDS/DeepTutor@3e82f13042 (2026-08-27). Data as JSON: /api/errors/3edeb74b2120e616. Report an issue: GitHub.