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

GitHub auth failed: {error}

Error message

GitHub auth failed: {error}

What it means

The GitHub device-flow token endpoint returned an error code other than the handled slow_down, authorization_pending, and expired_token cases. The raw GitHub error code is embedded in the message; common ones are access_denied (user denied the permission prompt), incorrect_device_code, and incorrect_client_id.

Source

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

                .send()
                .await?
                .json()
                .await?;

            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();

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. If the code is access_denied: the user must accept the permission prompt - restart the login
  2. For any other code, restart the device flow to obtain a fresh device_code
  3. If it persists, check whether your organization's OAuth app policy blocks the ZeroClaw GitHub app
  4. Alternatively configure a personal access token directly instead of device flow
Defensive patterns

Strategy: try-catch

Try / catch

match copilot_login().await {
    Ok(token) => { /* store token */ }
    Err(e) if e.to_string().contains("GitHub auth failed") => {
        // read the embedded code: access_denied needs user consent on retry;
        // anything else usually needs a fresh device code or a PAT fallback
        if e.to_string().contains("access_denied") {
            return Err(anyhow::anyhow!("user denied GitHub authorization; retry when ready"));
        }
        // else: restart the flow once with a fresh device code
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: User clicks cancel or deny on the GitHub authorization page; the device_code is replayed after expiry; GitHub rejects the OAuth client_id (suspended or blocked app); device flow disabled by policy for the account.

Common situations: User denies the requested scopes; a GitHub org policy restricts the OAuth app; retrying a flow with a stale device code; enterprise-managed accounts blocking third-party device flow.

Related errors


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