zed-industries/zed · error
The Copilot sign-in code expired. Please try again.
Error message
The Copilot sign-in code expired. Please try again.
What it means
Part of Zed's GitHub OAuth device-code flow (copilot_oauth.rs): after the user is shown a code at github.com/login/device, Zed polls the access-token endpoint; GitHub's error field 'expired_token' maps to this bail. GitHub device codes are valid for a short window (typically 15 minutes), after which the flow must be restarted from a new device code.
Source
Thrown at crates/copilot_chat/src/copilot_oauth.rs:132
.header("Content-Type", "application/x-www-form-urlencoded")
.body(AsyncBody::from(body.clone()))?;
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() {View on GitHub (pinned to f4178619ac)
Solutions
- Restart the Copilot sign-in flow to obtain a fresh device code
- Complete authorization at github.com/login/device promptly after the code is displayed
- Keep the machine/network awake during sign-in
Defensive patterns
Strategy: retry
Validate before calling
// Fail with a clearer message before GitHub does: device codes live ~15 min
let flow_started = std::time::Instant::now();
if flow_started.elapsed() > Duration::from_secs(14 * 60) {
anyhow::bail!("sign-in window nearly expired; restart the flow");
} Try / catch
match poll_for_token(/* .. */).await {
Ok(token) => { /* signed in */ }
Err(err) if err.to_string().contains("expired") => {
// restart device flow from scratch with a new code
}
Err(err) => return Err(err),
} Prevention
- Show the expiry window next to the device code so users hurry
- Restart the whole flow (new device code) on retry - a dead code can never be revived
- Avoid machine sleep or long awaits between starting the flow and user authorization
When it happens
Trigger: The polling loop receives error='expired_token': more than the validity window elapsed between displaying the user code and the user completing authorization, or polling was paused (machine slept, process suspended) past expiry.
Common situations: User leaves the sign-in dialog open and comes back later; laptop sleep during sign-in; user copies the code but completes verification too late.
Related errors
- Copilot sign-in was cancelled.
- Copilot sign-in failed: {other}
- Copilot sign-in failed: unexpected response from GitHub
- Failed to connect to API: {} {}
- Failed to connect to API: {} {}
AI-assisted analysis of zed-industries/zed@f4178619ac (2026-08-20).
Data as JSON: /api/errors/f984497fb89607c2.
Report an issue: GitHub.