zeroclaw-labs/zeroclaw · error · anyhow::Error

Timed out waiting for GitHub authorization

Error message

Timed out waiting for GitHub authorization

What it means

The device-flow poll loop ran until its expires_in deadline without the token endpoint returning success or a terminal error - the while condition Instant::now() < expires_at went false while polling was still effectively pending. This is the timeout backstop at the end of device_code_login, not a GitHub-reported error.

Source

Thrown at crates/zeroclaw-providers/src/copilot.rs:606

            if let Some(token) = token_response.access_token {
                eprintln!("Authentication succeeded.\n");
                return Ok(token);
            }

            match token_response.error.as_deref() {
                Some("slow_down") => {
                    poll_interval += Duration::from_secs(5);
                }
                Some("authorization_pending") | None => {}
                Some("expired_token") => {
                    anyhow::bail!("GitHub device authorization expired")
                }
                Some(error) => anyhow::bail!("GitHub auth failed: {error}"),
            }
        }

        anyhow::bail!("Timed out waiting for GitHub authorization")
    }

    /// Exchange a GitHub access token for a Copilot API key.
    async fn exchange_for_api_key(&self, access_token: &str) -> anyhow::Result<ApiKeyInfo> {
        let mut request = self.http_client().get(GITHUB_API_KEY_URL);
        for (header, value) in &Self::COPILOT_HEADERS {
            request = request.header(*header, *value);
        }
        request = request.header("Authorization", format!("token {access_token}"));

        let response = request.send().await?;

        if !response.status().is_success() {
            let status = response.status();
            let body = response.text().await.unwrap_or_default();
            let sanitized = super::sanitize_api_error(&body);

            if status.as_u16() == 401 || status.as_u16() == 403 {

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Re-run the login and authorize promptly when the code appears
  2. Ensure the system clock is correct (NTP) so the expiry window is computed accurately
  3. Check network stability to github.com - repeated slow polls can consume the window
  4. If it recurs, verify how long the terminal says the code is valid and complete the browser step immediately
Defensive patterns

Strategy: retry

Try / catch

match copilot_login().await {
    Ok(token) => { /* store token */ }
    Err(e) if e.to_string().contains("Timed out waiting for GitHub authorization") => {
        copilot_login().await // one restart: the previous code is dead anyway
    }
    Err(e) => Err(e),
}

Prevention

When it happens

Trigger: Polling authorization_pending for the full code lifetime without confirmation: user never completed authorization, poll intervals stretched by network latency, or a system clock jump made expires_at appear to pass early.

Common situations: User took longer than the code lifetime; NTP stepped the clock mid-flow; congested network stretching each poll cycle; user authorized just after the window closed.

Understand the failure class

Related errors


AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23). Data as JSON: /api/errors/e5f3601d42bf8b37. Report an issue: GitHub.