{"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":"info","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: McpHttpClient,\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 = client.send(request) => 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/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/mcp/sse.rs#L82-L118","documentation":"`run_sse_loop` waits for OAuth headers to resolve while listening on a cancellation token. If cancellation fires first (biased select gives it priority), the SSE connect is aborted with this error instead of proceeding or hanging.","triggerScenarios":"Calling the SSE connect with a `CancellationToken` that gets cancelled while `auth.resolved_headers()` is still pending — e.g. user aborts, app shutdown, or another task cancels during OAuth header resolution.","commonSituations":"Ctrl-C or disconnect while an MCP server is still completing OAuth; a supervisor cancelling a slow connect; retry logic cancelling a stale attempt.","solutions":["If this is unexpected, check what cancelled the token before the connect completed and delay cancellation until after connect","Treat this as a benign abort: catch it, exit the connect routine without retrying","Only cancel the token after deciding to abandon the connection; for timeouts use the connect timeout instead"],"exampleFix":"// before\ntokio::select! { _ = cancel.cancelled() => {}, res = connect(...) => res? }\n// after\nmatch connect(...).await {\n    Err(e) if e.to_string().contains(\"cancelled before authentication\") => { /* intentional abort */ }\n    other => other?,\n}","handlingStrategy":"try-catch","validationCode":"if cancel_token.is_cancelled() {\n    // don't even start the connect\n    return Ok(());\n}","typeGuard":null,"tryCatchPattern":"match run_sse_loop(...).await {\n    Err(e) if e.to_string().contains(\"cancelled\") => info!(\"connect aborted by user\"),\n    Err(e) => return Err(e),\n    Ok(v) => Ok(v),\n}","preventionTips":["Cancel tokens only after deciding to abandon the connection","Check token state before initiating connects"],"tags":["mcp","sse","cancellation","oauth"],"backgroundTag":"operation-cancelled","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}