xai-org/grok-build · error
Token exchange error: {detail}
Error message
Token exchange error: {detail} What it means
Catch-all branch: the token endpoint returned an unrecognized OAuth2 error code during device-code exchange. The client logs the raw error and detail and aborts with the server's description so the developer can diagnose the actual server-side cause.
Source
Thrown at crates/codegen/xai-grok-shell/src/auth/device_code.rs:278
"slow_down" => {
poll_interval += std::time::Duration::from_secs(DEVICE_SLOW_DOWN_INCREMENT_SECS);
continue;
}
"access_denied" => {
tracing::warn!(description = detail, "device auth authorization denied");
anyhow::bail!("Authorization denied. The user rejected the request.");
}
"expired_token" => {
tracing::warn!(description = detail, "device auth token expired");
anyhow::bail!("Device code expired. Run `grok login --device-auth` again.");
}
other => {
tracing::warn!(
error = other,
description = detail,
"device auth token exchange failed"
);
anyhow::bail!("Token exchange error: {detail}");
}
}
}
}
/// Device-code login shared by the TUI and CLI.
///
/// With `channels` (TUI) the verification URL goes to `url_tx` and the browser
/// opens automatically; on failure the copyable URL is the fallback. Without
/// `channels` (CLI) the URL + code are printed to stderr via `prompt_and_poll`.
/// `code_rx` is unused here. The caller reports success (`✓ Signed in`).
///
/// Takes `channels` by `&mut`, consuming it only after the device code is
/// obtained, so callers can reuse it for a loopback fallback on `NotEnabled`.
pub(crate) async fn run_device_code_login_channels(
issuer: &str,
client_id: &str,
scopes: &[String],View on GitHub (pinned to bc7f02eddd)
Solutions
- Read the `detail` in the message — it contains the server's error description.
- Verify the client_id/OAuth2 configuration (grok_com_config.oauth2) matches the issuer.
- Check the authorization server's status/logs for outages or gateway errors.
- Retry once with a fresh device code; if persistent, update the client or report the unhandled error code.
Example fix
// server response example causing it
{"error": "invalid_grant", "error_description": "device_code already used"}
// fix: do not reuse a device code; start a new login
grok login --device-auth Defensive patterns
Strategy: try-catch
Validate before calling
// pre-flight: confirm the token endpoint is reachable and returns JSON
curl -sS -o /dev/null -w '%{http_code}' https://x.ai/oauth/token Try / catch
match complete_device_code_login(&client, &pending).await {
Err(e) if e.to_string().starts_with("Token exchange error") => {
eprintln!("Server said: {e}. Check client_id config and issuer status, then retry.");
}
other => other?,
} Prevention
- Keep OAuth2 client_id/issuer config in sync with server expectations.
- Monitor the authorization server for gateway/proxy error injection.
- Never reuse a device_code after a successful or failed exchange.
- Update the client when the server introduces new OAuth2 error codes.
When it happens
Trigger: complete_device_code_login gets a token response whose error field is not one of authorization_pending, slow_down, access_denied, or expired_token — e.g. invalid_grant, invalid_client, or server_error.
Common situations: Client credentials/client_id mismatch after a config change; reverse proxy or gateway injecting error responses (502/503 HTML parsed as error); server API version drift introducing new error codes; device_code already consumed.
Related errors
- Server returned invalid user_code format (expected [A-Z0-9-]
- Device code expired. Run `grok login --device-auth` again.
- Authorization denied. The user rejected the request.
- Server returned invalid verification URI
- Server returned unsupported verification URI scheme
AI-assisted analysis of xai-org/grok-build@bc7f02eddd (2026-08-31).
Data as JSON: /api/errors/6bbee145922c340c.
Report an issue: GitHub.