{"record":{"id":"a00a5d7897717db6","repo":"Hmbown/CodeWhale","slug":"oauth-login-was-cancelled","errorCode":null,"errorMessage":"OAuth login was cancelled","messagePattern":"OAuth login was cancelled","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"info","filePath":"crates/tui/src/mcp/oauth.rs","lineNumber":462,"sourceCode":"        &cancellation_token,\n        perform_oauth_login_for_server_inner(\n            name,\n            server,\n            explicit_scopes,\n            callback_port,\n            callback_url,\n        ),\n    )\n    .await\n}\n\nasync fn run_cancellable_oauth<F, T>(cancellation_token: &CancellationToken, future: F) -> Result<T>\nwhere\n    F: std::future::Future<Output = Result<T>>,\n{\n    tokio::select! {\n        biased;\n        _ = cancellation_token.cancelled() => bail!(\"OAuth login was cancelled\"),\n        result = future => result,\n    }\n}\n\nasync fn perform_oauth_login_for_server_inner(\n    name: &str,\n    server: &McpServerConfig,\n    explicit_scopes: Option<Vec<String>>,\n    callback_port: Option<u16>,\n    callback_url: Option<&str>,\n) -> Result<()> {\n    let Some(url) = server.url.as_deref() else {\n        bail!(\"OAuth login is only supported for URL-based MCP servers\");\n    };\n    if server_has_manual_authorization(server) {\n        bail!(\"MCP server '{name}' already has bearer/static Authorization configured\");\n    }\n","sourceCodeStart":444,"sourceCodeEnd":480,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/tui/src/mcp/oauth.rs#L444-L480","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["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","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","Re-run the OAuth login command without triggering cancellation to complete the flow"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"match perform_oauth_login_for_server(&name, &server, None, None, None).await {\n    Err(err) if err.to_string().contains(\"OAuth login was cancelled\") => {\n        tracing::info!(target: \"mcp\", \"login aborted by user\");\n    }\n    Err(err) => return report_login_failure(name, err),\n    Ok(()) => {}\n}","preventionTips":["Distinguish cancellation from failure in the catch: cancelled logins should be info-level and non-blocking","Keep UI cancel tokens scoped to the dialog that owns them so unrelated shutdowns cannot abort an active login","Surface a non-modal 'login cancelled' notice instead of an error toast"],"tags":["oauth","mcp","cancellation","tokio","rust"],"backgroundTag":"operation-cancelled","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}