zed-industries/zed · error
OAuth authorization failed: {} ({})
Error message
OAuth authorization failed: {} ({}) What it means
Zed's local OAuth callback server parses the provider's redirect query string. Per the OAuth2 error-response convention (RFC 6749), presence of an `error` parameter means the authorization attempt itself failed; the code embeds it plus `error_description` (or 'no description' when absent) and never returns a code.
Source
Thrown at crates/oauth_callback_server/src/oauth_callback_server.rs:206
state = Some(value.into_owned());
}
}
"error" => {
if !value.is_empty() {
error = Some(value.into_owned());
}
}
"error_description" => {
if !value.is_empty() {
error_description = Some(value.into_owned());
}
}
_ => {}
}
}
if let Some(error_code) = error {
anyhow::bail!(
"OAuth authorization failed: {} ({})",
error_code,
error_description.as_deref().unwrap_or("no description")
);
}
let code = code.ok_or_else(|| anyhow!("missing 'code' parameter in OAuth callback"))?;
let state =
state.ok_or_else(|| anyhow!("missing 'state' parameter in OAuth callback"))?;
Ok(Self { code, state })
}
}
/// How long to wait for the browser to complete the OAuth flow before giving
/// up and releasing the loopback port.
const OAUTH_CALLBACK_TIMEOUT: Duration = Duration::from_secs(2 * 60);
View on GitHub (pinned to f4178619ac)
Solutions
- Retry the sign-in and approve the consent screen — access_denied is usually just a cancelled flow
- Verify the client_id/client_secret and registered redirect URI in the provider's app console match what Zed uses
- Check error_description in the message for the provider's exact reason and act on that code
- Re-start the OAuth flow to get a fresh state if the error mentions expired/invalid state
Defensive patterns
Strategy: try-catch
Validate before calling
// before opening the browser, confirm provider app config
fn provider_config_plausible(client_id: &str, redirect_uri: &Url) -> bool {
!client_id.is_empty() && redirect_uri.scheme() == "http" && redirect_uri.host_str() == Some("localhost")
} Try / catch
match OAuthCallbackParams::parse_query(query) {
Err(e) if e.to_string().contains("OAuth authorization failed") => {
let code = extract_error_code(&e); // access_denied vs invalid_client
if code == "access_denied" {
show("Sign-in was cancelled"); // benign, allow retry
} else {
show_provider_config_guidance(&e); // check client id / redirect URI
}
}
r => r?,
} Prevention
- Register the exact loopback redirect URI in the provider app console
- Treat access_denied as user cancellation, not an error state
- Include the provider's error_description in diagnostics; it names the real cause
- Start a fresh flow (new state) rather than replaying old authorize URLs
When it happens
Trigger: The provider redirects back with error=access_denied (user clicked Deny/Cancel), invalid_client, invalid_request, or temporarily_unavailable in the query string — e.g. denying the consent screen, a mismatched client_id/secret, or a mismatched redirect URI registered with the provider.
Common situations: Cancelling the sign-in consent page; stale/incorrect client credentials configured for the integration; redirect URI registered in the provider app not matching the loopback callback; clock/state mismatches surfacing as invalid_request.
Related errors
- authorization server does not support S256 PKCE
- The Copilot sign-in code expired. Please try again.
- Message method is undefined: ${JSON.stringify(message)}
- didn't receive login redirect
- OAuth endpoint must use HTTPS (got {}://{})
AI-assisted analysis of zed-industries/zed@f4178619ac (2026-08-20).
Data as JSON: /api/errors/31f62fbc6ea4e4ed.
Report an issue: GitHub.