BloopAI/vibe-kanban · error
github token exchange failed: {detail}
Error message
github token exchange failed: {detail} What it means
During the GitHub OAuth code-for-token exchange, GitHub responded with an error variant in its token response (e.g. bad_verification_code, redirect_uri mismatch) instead of an access token. The provider surfaces GitHub's own error (or its description) in the message.
Source
Thrown at crates/remote/src/auth/provider.rs:222
match response.json::<GitHubTokenResponse>().await? {
GitHubTokenResponse::Success {
access_token,
scope,
token_type,
} => Ok(AuthorizationGrant {
access_token: SecretString::new(access_token.into()),
token_type,
scopes: Self::parse_scopes(scope),
refresh_token: None,
expires_in: None,
id_token: None,
}),
GitHubTokenResponse::Error {
error,
error_description,
} => {
let detail = error_description.unwrap_or_else(|| error.clone());
anyhow::bail!("github token exchange failed: {detail}")
}
}
}
async fn fetch_user(&self, access_token: &SecretString) -> Result<ProviderUser> {
let bearer = format!("Bearer {}", access_token.expose_secret());
let user: GitHubUser = self
.client
.get("https://api.github.com/user")
.header("Accept", "application/vnd.github+json")
.header("Authorization", &bearer)
.send()
.await?
.error_for_status()?
.json()
.await?;
View on GitHub (pinned to 4deb7eca8f)
Solutions
- Restart the OAuth flow to obtain a fresh authorization code
- Verify client_id, client_secret, and redirect_uri match between the auth URL and token request
- Ensure the code is exchanged exactly once and immediately after redirect
- Check GitHub OAuth app settings in the developer portal
Example fix
// before // exchanging a code twice let t1 = provider.exchange_code(&code).await?; let t2 = provider.exchange_code(&code).await?; // github token exchange failed: bad_verification_code // after let t = provider.exchange_code(&code).await?; // exchange exactly once
Defensive patterns
Strategy: retry
Try / catch
match provider.exchange_code(&code).await { Err(e) if e.to_string().starts_with("github token exchange failed") => restart_oauth_flow(), // codes are single-use; never retry with same code
other => other? } Prevention
- Exchange the code immediately and exactly once
- Keep redirect_uri, client_id, client_secret identical across authorize/token steps
- Rotate long-lived flows through refresh tokens instead of reusing codes
- Match OAuth app settings in the GitHub developer portal
When it happens
Trigger: exchange_code() called with an expired, already-used, or invalid authorization code; GitHub returns error/error_description in the token response body.
Common situations: User took too long to authorize so the code expired; code replayed on a second exchange; mismatched client_id/client_secret or redirect_uri between the authorize and token requests; clock skew invalidating signed requests.
Related errors
- google 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/c8c740dddf635eed.
Report an issue: GitHub.