Hmbown/CodeWhale · info

OAuth login was cancelled

Error message

OAuth login was cancelled

What it means

Thrown by run_cancellable_oauth in crates/tui/src/mcp/oauth.rs when the CancellationToken fires before the OAuth login future completes. The tokio::select! is biased, so cancellation always wins over the login flow even if the browser callback is milliseconds away. This is a controlled, user-initiated abort of the MCP OAuth login flow, not a failure of the OAuth exchange itself, and no tokens are stored.

Source

Thrown at crates/tui/src/mcp/oauth.rs:462

        &cancellation_token,
        perform_oauth_login_for_server_inner(
            name,
            server,
            explicit_scopes,
            callback_port,
            callback_url,
        ),
    )
    .await
}

async fn run_cancellable_oauth<F, T>(cancellation_token: &CancellationToken, future: F) -> Result<T>
where
    F: std::future::Future<Output = Result<T>>,
{
    tokio::select! {
        biased;
        _ = cancellation_token.cancelled() => bail!("OAuth login was cancelled"),
        result = future => result,
    }
}

async fn perform_oauth_login_for_server_inner(
    name: &str,
    server: &McpServerConfig,
    explicit_scopes: Option<Vec<String>>,
    callback_port: Option<u16>,
    callback_url: Option<&str>,
) -> Result<()> {
    let Some(url) = server.url.as_deref() else {
        bail!("OAuth login is only supported for URL-based MCP servers");
    };
    if server_has_manual_authorization(server) {
        bail!("MCP server '{name}' already has bearer/static Authorization configured");
    }

View on GitHub (pinned to 0c42157ee5)

Solutions

  1. Treat it as a benign abort: log at info/debug level and return the user to the MCP server list; do not surface it as a failure
  2. If the cancellation is unexpected, find what calls CancellationToken::cancel() on this token (shutdown path, dialog dismiss handler) and decouple it from the login flow
  3. Re-run the OAuth login command without triggering cancellation to complete the flow
Defensive patterns

Strategy: try-catch

Try / catch

match perform_oauth_login_for_server(&name, &server, None, None, None).await {
    Err(err) if err.to_string().contains("OAuth login was cancelled") => {
        tracing::info!(target: "mcp", "login aborted by user");
    }
    Err(err) => return report_login_failure(name, err),
    Ok(()) => {}
}

Prevention

When it happens

Trigger: perform_oauth_login_for_server (e.g. the TUI's MCP login command) is awaiting browser authorization or token exchange while the user presses the cancel/Escape key, the session shuts down, or anything else cancels the shared CancellationToken passed into run_cancellable_oauth.

Common situations: User closes or aborts the OAuth prompt before finishing authorization in the browser; app shutdown or TUI exit races an in-flight login; a UI timeout wired to the same cancellation token fires during a slow authorization-code callback.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20). Data as JSON: /api/errors/a00a5d7897717db6. Report an issue: GitHub.