zed-industries/zed · error

Copilot sign-in failed: {other}

Error message

Copilot sign-in failed: {other}

What it means

Catch-all for device-flow error codes GitHub returns that are not authorization_pending, slow_down, expired_token, or access_denied. The literal error code from GitHub is interpolated into 'Copilot sign-in failed: {other}'. These come from RFC-compatible device-authorization endpoints (e.g. incorrect_device_code, incorrect_client_credentials, unsupported_grant_type).

Source

Thrown at crates/copilot_chat/src/copilot_oauth.rs:134

        let mut response = client.send(request).await?;
        let mut response_body = Vec::new();
        response.body_mut().read_to_end(&mut response_body).await?;

        let parsed: AccessTokenResponse = serde_json::from_slice(&response_body)
            .context("Failed to parse GitHub access-token response")?;

        if let Some(token) = parsed.access_token {
            return Ok(token);
        }

        match parsed.error.as_deref() {
            Some("authorization_pending") => continue,
            // GitHub asks us to back off; increase the interval and keep polling.
            Some("slow_down") => interval += 5,
            Some("expired_token") => bail!("The Copilot sign-in code expired. Please try again."),
            Some("access_denied") => bail!("Copilot sign-in was cancelled."),
            Some(other) => bail!("Copilot sign-in failed: {other}"),
            None => bail!("Copilot sign-in failed: unexpected response from GitHub"),
        }
    }
}

fn form_encode(fields: &[(&str, &str)]) -> String {
    fields
        .iter()
        .map(|(key, value)| format!("{}={}", url_encode(key), url_encode(value)))
        .collect::<Vec<_>>()
        .join("&")
}

fn url_encode(value: &str) -> String {
    let mut encoded = String::with_capacity(value.len());
    for byte in value.bytes() {
        match byte {
            b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'-' | b'_' | b'.' | b'~' => {

View on GitHub (pinned to f4178619ac)

Solutions

  1. Note the embedded error code; for incorrect_device_code, restart the flow and abandon the old code
  2. Ensure only one sign-in flow is active per client at a time
  3. For credential/grant errors, update Zed so the client_id/grant it sends matches GitHub's current requirements
  4. Retry sign-in from scratch
Defensive patterns

Strategy: try-catch

Validate before calling

// Serialize flows: one device code at a time
static FLOW_LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(());
let _guard = FLOW_LOCK.lock().unwrap_or_else(|p| p.into_inner());

Try / catch

match poll_for_token(/* .. */).await {
    Ok(token) => Ok(token),
    Err(err) => {
        let msg = err.to_string();
        if msg.contains("incorrect_device_code") { /* restart flow */ }
        else { Err(err.context("unexpected device-flow error")) }
    }
}

Prevention

When it happens

Trigger: The token poll receives an unrecognized error code: polling after the device code was superseded by a new flow (incorrect_device_code), client credentials mismatch (incorrect_client_credentials), or the endpoint semantics changed (unsupported_grant_type / unsupported_token_type).

Common situations: Two sign-in attempts racing (each new device code invalidates the old one); clock skew or long pauses between polls; GitHub changing device-flow error semantics; malformed intermediate proxies altering responses.

Related errors


AI-assisted analysis of zed-industries/zed@f4178619ac (2026-08-20). Data as JSON: /api/errors/cf628ef1691450df. Report an issue: GitHub.