Hmbown/CodeWhale · error

SSE endpoint not received within {}ms

Error message

SSE endpoint not received within {}ms

What it means

After connecting, the transport waits a bounded endpoint_timeout for the server's endpoint event, which declares the POST URL for messages. No endpoint event arrived before the deadline, so the transport gives up; the configured timeout in milliseconds is printed in the message.

Source

Thrown at crates/tui/src/mcp/sse.rs:218

            }
        }
        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");
            }
        }
    }

    fn store_endpoint(&mut self, endpoint: &str) -> Result<()> {

View on GitHub (pinned to 8880682c63)

Solutions

  1. Verify with curl -N <url> that an `event: endpoint` line arrives immediately after connect
  2. Match transport to endpoint: /sse with transport=sse, /mcp with transport=http
  3. Disable proxy buffering for event-stream responses (proxy_buffering off; X-Accel-Buffering: no)
  4. If the server is genuinely slow to handshake, fix its startup latency or raise the endpoint timeout
Defensive patterns

Strategy: validation

Validate before calling

```rust
// Before enabling a server: does the first streamed bytes contain `event: endpoint`?
async fn announces_endpoint(client: &reqwest::Client, url: &str) -> bool {
    use futures_util::StreamExt;
    let stream = match client.get(url).send().await { Ok(r) => r.bytes_stream(), Err(_) => return false };
    tokio::pin!(stream);
    let mut buf = Vec::new();
    let deadline = tokio::time::Duration::from_secs(3);
    while let Ok(Some(Ok(chunk))) = tokio::time::timeout(deadline, stream.next()).await {
        buf.extend_from_slice(&chunk);
        if buf.windows(15).any(|w| w == b"event: endpoint") { return true; }
    }
    false
}
```

Try / catch

```rust
match transport.wait_for_endpoint(&cancel, timeout).await {
    Err(e) if e.to_string().contains("endpoint not received within") => {
        fix_transport_or_url(); // wrong endpoint or buffering: do not blind-retry
    }
    other => other?,
}
```

Prevention

When it happens

Trigger: A 200 response that never emits `event: endpoint` within the timeout: pointing transport=sse at a streamable-http /mcp route, a plain HTML page, or a proxy that buffers text/event-stream so events arrive late or never.

Common situations: Transport/URL mismatch; nginx/CDN buffering disabling streaming (missing proxy_buffering off); server that stalls before the handshake, e.g. waiting on auth.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@8880682c63 (2026-08-16). Data as JSON: /api/errors/d49078bd2768389d. Report an issue: GitHub.