{"record":{"id":"2af8b50ea45a57a8","repo":"Hmbown/CodeWhale","slug":"sse-transport-cancelled-before-endpoint-was-discov","errorCode":null,"errorMessage":"SSE transport cancelled before endpoint was discovered","messagePattern":"SSE transport cancelled before endpoint was discovered","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"crates/tui/src/mcp/sse.rs","lineNumber":215,"sourceCode":"                        return Ok(());\n                    }\n                }\n            }\n        }\n        Ok(())\n    }\n\n    async fn wait_for_endpoint(\n        &mut self,\n        cancel_token: &tokio_util::sync::CancellationToken,\n        endpoint_timeout: Duration,\n    ) -> Result<()> {\n        let timeout = tokio::time::sleep(endpoint_timeout);\n        tokio::pin!(timeout);\n\n        let msg = tokio::select! {\n            _ = cancel_token.cancelled() => {\n                anyhow::bail!(\"SSE transport cancelled before endpoint was discovered\");\n            }\n            _ = &mut timeout => {\n                anyhow::bail!(\n                    \"SSE endpoint not received within {}ms\",\n                    endpoint_timeout.as_millis()\n                );\n            }\n            msg = self.receiver.recv() => {\n                msg.context(\"SSE transport closed before endpoint was discovered\")?\n            }\n        };\n\n        match msg {\n            SseInbound::Endpoint(endpoint) => self.store_endpoint(&endpoint),\n            SseInbound::Message(_) => {\n                anyhow::bail!(\"MCP SSE server sent a message before declaring its endpoint\");\n            }\n        }","sourceCodeStart":197,"sourceCodeEnd":233,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/8880682c63083a91624de936797efa3ce9e498fd/crates/tui/src/mcp/sse.rs#L197-L233","documentation":"After a successful connect, the transport waits for the server's mandatory endpoint event. If the CancellationToken fires during that wait, this bail aborts cleanly — the connection was torn down (shutdown, reconnect, disable) before the SSE handshake finished.","triggerScenarios":"Session close or MCP server disable in the window between HTTP connect success and receipt of the first endpoint event.","commonSituations":"User quits while a slow server is still emitting its first event; retry logic cancelling the previous attempt too early.","solutions":["Treat as cancellation noise during teardown; confirm the cancel() was intended","If it recurs outside shutdown, inspect reconnect logic for premature cancellation of healthy connects"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":"```rust\nfn is_endpoint_wait_cancelled(err: &anyhow::Error) -> bool {\n    format!(\"{err:#}\").contains(\"SSE transport cancelled before endpoint was discovered\")\n}\n```","tryCatchPattern":"```rust\nmatch transport.wait_for_endpoint(&cancel, timeout).await {\n    Err(e) if is_endpoint_wait_cancelled(&e) => return Ok(()), // teardown, not failure\n    other => other?,\n}\n```","preventionTips":["Sequence shutdown before the handshake window when possible: cancel prior to connect, not after it","Distinguish cancellation from timeout in logs to avoid false server-failure alerts"],"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"}