BloopAI/vibe-kanban · error
google token exchange failed: {detail}
Error message
google token exchange failed: {detail} What it means
During the Google OAuth code-for-token exchange, Google's token endpoint returned an error variant (e.g. invalid_grant, redirect_uri_mismatch) instead of tokens. The provider wraps Google's error code or description into this bail message.
Source
Thrown at crates/remote/src/auth/provider.rs:571
(!trimmed.is_empty()).then_some(trimmed.to_string())
})
.collect();
Ok(AuthorizationGrant {
access_token: SecretString::new(access_token.into()),
token_type,
scopes,
refresh_token: refresh_token.map(|v| SecretString::new(v.into())),
expires_in: expires_in.map(Duration::seconds),
id_token: id_token.map(|v| SecretString::new(v.into())),
})
}
GoogleTokenResponse::Error {
error,
error_description,
} => {
let detail = error_description.unwrap_or_else(|| error.clone());
anyhow::bail!("google token exchange failed: {detail}")
}
}
}
async fn fetch_user(&self, access_token: &SecretString) -> Result<ProviderUser> {
let bearer = format!("Bearer {}", access_token.expose_secret());
let profile: GoogleUser = self
.client
.get("https://openidconnect.googleapis.com/v1/userinfo")
.header("Authorization", bearer)
.send()
.await?
.error_for_status()?
.json()
.await?;
let login = profile.email.clone();View on GitHub (pinned to 4deb7eca8f)
Solutions
- Restart the OAuth flow to get a fresh authorization code and exchange it immediately
- Ensure the redirect_uri exactly matches one registered in Google Cloud Console
- Verify client_id/client_secret for the correct Google OAuth client/environment
- If using offline access, request the correct scopes/access_type and use refresh_token grant appropriately
Example fix
// before // token request redirect_uri = "http://localhost:3000/callback" // authorize request redirect_uri = "https://app.example.com/callback" -> invalid_grant / redirect_uri_mismatch // after // use the identical redirect_uri in both authorize and token requests
Defensive patterns
Strategy: retry
Try / catch
match provider.exchange_code(&code).await { Err(e) if e.to_string().starts_with("google token exchange failed") => restart_oauth_flow(), other => other? } Prevention
- Exchange Google codes promptly (they expire in minutes)
- Register and use the exact redirect_uri in Google Cloud Console
- Verify client secret per environment
- Never reuse an authorization code; start a new flow for retries
When it happens
Trigger: exchange_code() on the Google provider receives GoogleTokenResponse::Error — typically because the code is expired/used, or redirect_uri/client credentials don't match the original request.
Common situations: Authorization code expired (Google codes are short-lived, ~10 min) or already redeemed; redirect URI not whitelisted in Google Cloud Console; wrong client secret between environments (staging vs prod); refresh-token flow attempted with the wrong grant type.
Related errors
- github token exchange failed: {detail}
- OAuth init failed (${res.status})
- OAuth redeem failed (${res.status})
- Host returned HTTP ${response.status}
- Auth methods lookup failed (${res.status})
AI-assisted analysis of BloopAI/vibe-kanban@4deb7eca8f (2026-08-29).
Data as JSON: /api/errors/cc8dcaacb1435da4.
Report an issue: GitHub.