xai-org/grok-build · error · OidcError
OidcError::CallbackChannelClosed
Error message
OidcError::CallbackChannelClosed
What it means
In `race_callback_and_client_ui`, when the timeout branch's inner `rx.recv()` returns None, the mpsc channel to the callback handler has been closed without ever producing an authorization code. The error distinguishes 'channel closed, no code' from an actual timeout: the callback receiver side dropped before delivering a code.
Source
Thrown at crates/codegen/xai-grok-shell/src/auth/oidc/login.rs:286
match parse_pasted_input(&code) {
Ok(result) => {
tracing::debug!("OIDC: received code via client paste");
let _ = client_tx.send(Ok(result)).await;
return;
}
Err(e) => {
tracing::debug!(error = %e, "OIDC: invalid client paste input");
}
}
}
};
drop(tx);
let result = tokio::select! {
r = tokio::time::timeout(AUTH_CALLBACK_TIMEOUT, rx.recv()) => {
r.map_err(|_| anyhow::Error::new(OidcError::CallbackTimeout))?
.ok_or_else(|| anyhow::Error::new(OidcError::CallbackChannelClosed))?
}
_ = client_bridge => {
rx.recv().await
.ok_or_else(|| anyhow::Error::new(OidcError::CallbackChannelClosed))?
}
};
let _ = shutdown_tx.send(());
let _ = server.await;
result.map_err(|e| anyhow::Error::new(OidcError::CallbackAuthFailed(e)))
}
/// Race loopback callback against stdin paste.
async fn race_callback_and_stdin(
listener: TcpListener,
enable_stdin: bool,
) -> anyhow::Result<Callback> {View on GitHub (pinned to bc7f02eddd)
Solutions
- Retry the login; transient handler shutdowns are usually resolved by a fresh run.
- Check server logs for the loopback callback handler panicking or rejecting the request (e.g. missing code/state query params).
- Verify the IDP redirects back with the expected `code` (and `state`) query parameters; a misconfigured redirect_uri can yield an error response that closes the channel.
- If the client UI bridge is shutting down the flow, confirm the UI stays alive until login completes.
Defensive patterns
Strategy: try-catch
Type guard
fn is_channel_closed(err: &anyhow::Error) -> bool {
err.downcast_ref::<OidcError>()
.map_or(false, |e| matches!(e, OidcError::CallbackChannelClosed))
} Try / catch
if is_channel_closed(&err) {
tracing::warn!("callback channel closed before code delivery; retrying login");
return run_login_flow(config, auth_manager, channels).await; // one retry
} Prevention
- Keep the client UI open until login completes
- Verify the IDP redirect includes `code` and `state` query params
- Watch logs for loopback handler panics that drop the sender
- Retry once before surfacing the error to the user
When it happens
Trigger: AUTH_CALLBACK_TIMEOUT elapses is excluded here; specifically the timeout future completes Ok(None) — i.e. rx.recv() yields None because the sender (the loopback HTTP handler or client UI bridge) was dropped without sending a code.
Common situations: The loopback server task panicked or was aborted after shutdown_tx fired; the browser hit /callback with a malformed request and the handler dropped tx without sending; the client UI bridge shut down the flow early.
Related errors
- OidcError::CallbackTimeout
- PTY write channel closed
- terminal writer stopped
- no OIDC auth entry found in {}. Run `grok login` first.
- auth entry has no refresh_token — cannot refresh expired tok
AI-assisted analysis of xai-org/grok-build@bc7f02eddd (2026-08-31).
Data as JSON: /api/errors/27bce532b3662858.
Report an issue: GitHub.