Hmbown/CodeWhale · info
SSE transport cancelled before endpoint was discovered
Error message
SSE transport cancelled before endpoint was discovered
What it means
`wait_for_endpoint` blocks until the SSE server declares its message endpoint. If the cancellation token fires while waiting, the wait is aborted with this error (this branch takes priority over the timeout and channel receive in the select).
Solutions
- If unexpected, find what cancels the token during startup and defer it until after handshake
- Treat as benign teardown: catch and exit without retrying
- If the server is slow to send the endpoint event, fix the server or raise the endpoint timeout
Defensive patterns
Strategy: try-catch
Try / catch
match wait_for_endpoint(...).await {
Err(e) if e.to_string().contains("cancelled before endpoint") => info!("aborted during handshake"),
other => other?,
} Prevention
- Keep cancellation tokens alive through the full handshake; cancel only on real teardown
When it happens
Trigger: Cancelling the token after the SSE stream connected but before the server sent its `endpoint` event.
Common situations: Shutdown or user abort mid-handshake; supervisor tearing down a hung server that never sent the endpoint event.
Related errors
- MCP SSE connect cancelled before authentication completed
- MCP SSE connect cancelled before the request completed
- MCP connection ' ' was cancelled
- MCP connection ' ' was cancelled
- MCP operation on plugin server
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/2af8b50ea45a57a8.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/mcp/sse.rs:215
return Ok(());
}
}
}
}
Ok(())
}
async fn wait_for_endpoint(
&mut self,
cancel_token: &tokio_util::sync::CancellationToken,
endpoint_timeout: Duration,
) -> Result<()> {
let timeout = tokio::time::sleep(endpoint_timeout);
tokio::pin!(timeout);
let msg = tokio::select! {
_ = cancel_token.cancelled() => {
anyhow::bail!("SSE transport cancelled before endpoint was discovered");
}
_ = &mut timeout => {
anyhow::bail!(
"SSE endpoint not received within {}ms",
endpoint_timeout.as_millis()
);
}
msg = self.receiver.recv() => {
msg.context("SSE transport closed before endpoint was discovered")?
}
};
match msg {
SseInbound::Endpoint(endpoint) => self.store_endpoint(&endpoint),
SseInbound::Message(_) => {
anyhow::bail!("MCP SSE server sent a message before declaring its endpoint");
}
}View on GitHub (pinned to 73e0f67d83)