gitbutlerapp/gitbutler · error · anyhow::Error
GitHub returned an error: {} ({})
Error message
GitHub returned an error: {} ({}) What it means
GitHub's OAuth endpoints signal failures by returning JSON with an `error` code (plus `error_description`) instead of the expected payload. This parser detects that shape before deserializing, so the real code - device_flow_disabled, authorization_pending, access_denied, incorrect_client_credentials - surfaces instead of a generic serde 'missing field' error.
Source
Thrown at crates/but-github/src/lib.rs:40
#[cfg_attr(feature = "export-schema", derive(schemars::JsonSchema))]
#[derive(Debug, Deserialize, Serialize, Clone, Default)]
pub struct Verification {
pub user_code: String,
pub device_code: String,
}
#[cfg(feature = "export-schema")]
but_schemars::register_sdk_type!(Verification);
/// Detect GitHub's OAuth error shape (e.g. `device_flow_disabled`, `authorization_pending`) before falling back to the expected payload, so the real cause surfaces instead of a generic "missing field" serde error.
fn parse_github_oauth_response<T: serde::de::DeserializeOwned>(body: &str) -> Result<T> {
let value: serde_json::Value =
serde_json::from_str(body).context("Response body was not valid JSON")?;
if let Some(error) = value.get("error").and_then(serde_json::Value::as_str) {
let description = value
.get("error_description")
.and_then(serde_json::Value::as_str);
anyhow::bail!(
"GitHub returned an error: {} ({})",
error,
description.unwrap_or("no description"),
);
}
serde_json::from_value(value).context("Response body did not match expected schema")
}
pub async fn init_github_device_oauth() -> Result<Verification> {
let mut req_body = HashMap::new();
let app_settings = AppSettings::load_from_default_path_creating_without_customization()?;
let client_id = app_settings.github_oauth_app.oauth_client_id.clone();
req_body.insert("client_id", client_id.as_str());
req_body.insert("scope", "repo");
let mut headers = reqwest::header::HeaderMap::new();
headers.insert(
reqwest::header::ACCEPT,View on GitHub (pinned to caf1f223d3)
Solutions
- authorization_pending/slow_down: keep polling with the interval GitHub told you - this is normal device flow, not a failure
- device_flow_disabled: use an OAuth app that permits device flow
- access_denied: restart the flow and approve the consent screen
- incorrect_client_credentials: fix the OAuth app client id in settings
Example fix
// before
let token = poll_device_token(&verification).await?;
// after
match poll_device_token(&verification).await {
Err(e) if e.to_string().contains("authorization_pending") => {
tokio::time::sleep(interval).await;
continue;
}
Err(e) if e.to_string().contains("slow_down") => {
interval = interval.mul_f64(1.5);
tokio::time::sleep(interval).await;
continue;
}
other => other?,
} Defensive patterns
Strategy: retry
Type guard
fn is_retryable_oauth_error(err: &anyhow::Error) -> bool {
let msg = err.to_string();
msg.contains("authorization_pending") || msg.contains("slow_down")
} Try / catch
loop {
match poll_device_flow_token(&verification).await {
Ok(token) => break Ok(token),
Err(e) if e.to_string().contains("authorization_pending") => {
tokio::time::sleep(interval).await
}
Err(e) if e.to_string().contains("slow_down") => {
interval = interval.mul_f64(1.5);
tokio::time::sleep(interval).await
}
Err(e) => break Err(e), // access_denied, device_flow_disabled, ... are terminal
}
} Prevention
- Poll only at the interval GitHub returns, never faster
- Treat only authorization_pending and slow_down as retryable device-flow codes
- Fix the OAuth client id before shipping if you see incorrect_client_credentials
When it happens
Trigger: Device-flow token polling before the user approves (authorization_pending, slow_down); an OAuth app with device flow disabled (device_flow_disabled); the user denies the consent screen (access_denied); a wrong oauth_client_id in settings (incorrect_client_credentials).
Common situations: Polling the token endpoint too eagerly during device flow; org-restricted OAuth apps; misconfigured OAuth client ids in app settings.
Related errors
- The browser did not finish signing in
- GitHub GraphQL request failed: {}
- No authenticated GitHub users found.\nRun 'but config forge
- Preferred GitHub account '{account}' has not authenticated y
- No GitHub access token found for account '{account_id}'. Run
AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20).
Data as JSON: /api/errors/58290a47c23caa5c.
Report an issue: GitHub.