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

  1. Retry the login; transient handler shutdowns are usually resolved by a fresh run.
  2. Check server logs for the loopback callback handler panicking or rejecting the request (e.g. missing code/state query params).
  3. 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.
  4. 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

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


AI-assisted analysis of xai-org/grok-build@bc7f02eddd (2026-08-31). Data as JSON: /api/errors/27bce532b3662858. Report an issue: GitHub.