{"record":{"id":"bf9573d852b8a652","repo":"Hmbown/CodeWhale","slug":"mcp-sse-connect-cancelled-before-authentication-co","errorCode":null,"errorMessage":"MCP SSE connect cancelled before authentication completed","messagePattern":"MCP SSE connect cancelled before authentication completed","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"crates/tui/src/mcp/sse.rs","lineNumber":100,"sourceCode":"            sse_task,\n        };\n        transport\n            .wait_for_endpoint(&wait_cancel_token, endpoint_timeout)\n            .await?;\n        Ok(transport)\n    }\n\n    async fn run_sse_loop(\n        client: reqwest::Client,\n        url: String,\n        auth: McpHttpAuth,\n        tx: tokio::sync::mpsc::Sender<SseInbound>,\n        cancel_token: tokio_util::sync::CancellationToken,\n    ) -> Result<()> {\n        let headers = tokio::select! {\n            biased;\n            _ = cancel_token.cancelled() => {\n                anyhow::bail!(\"MCP SSE connect cancelled before authentication completed\")\n            }\n            headers = auth.resolved_headers() => headers?,\n        };\n        let request = apply_safe_custom_headers(\n            with_default_mcp_http_headers(client.get(&url), false),\n            &headers,\n        );\n        let response = tokio::select! {\n            biased;\n            _ = cancel_token.cancelled() => {\n                anyhow::bail!(\"MCP SSE connect cancelled before the request completed\")\n            }\n            response = request.send() => response.with_context(|| {\n                format!(\n                    \"MCP SSE connect failed (transport=http url={})\",\n                    mask_url_secrets(&url),\n                )\n            })?,","sourceCodeStart":82,"sourceCodeEnd":118,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/8880682c63083a91624de936797efa3ce9e498fd/crates/tui/src/mcp/sse.rs#L82-L118","documentation":"While establishing an SSE connection, the transport races its CancellationToken against auth.resolved_headers(), which may perform an OAuth handshake or token refresh. If the token fires first (shutdown, disable, reconnect), this bail aborts the connect cleanly instead of letting the in-flight auth work leak. It is an expected lifecycle path, not a defect.","triggerScenarios":"Quitting the app or toggling an MCP server off while the SSE connect is still resolving OAuth headers; a reconnect loop that tears down a transport before its first connect completes.","commonSituations":"App shutdown during a slow OAuth token fetch; rapidly enabling/disabling MCP servers; cancellation cascades from a parent session restart.","solutions":["Treat it as a normal cancellation — confirm a shutdown or reconnect was actually intended","If unexpected, trace who called cancel_token.cancel() (session close, server disable) before auth finished","Avoid spawning a transport and cancelling it immediately; either cancel before starting or let the connect settle"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":"```rust\nfn is_auth_cancelled(err: &anyhow::Error) -> bool {\n    format!(\"{err:#}\").contains(\"MCP SSE connect cancelled before authentication completed\")\n}\n```","tryCatchPattern":"```rust\nmatch sse_transport.connect().await {\n    Err(e) if is_auth_cancelled(&e) => return Ok(()), // shutdown path: exit quietly\n    other => other?,\n}\n```","preventionTips":["Cancel transports only before spawning them or after connect settles, never mid-handshake","Log cancellation bails at debug level so shutdown traces stay readable"],"tags":["mcp","sse","cancellation","lifecycle"],"backgroundTag":null,"analyzedSha":"8880682c63083a91624de936797efa3ce9e498fd","analyzedAt":"2026-08-16T11:31:27.956Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}