Kuberwastaken/claurst · error

legacy SSE stream closed before announcing endpoint

Error message

legacy SSE stream closed before announcing endpoint

What it means

The legacy SSE stream task ended (Ok) without ever receiving the `endpoint` event that announces the POST URL. Endpoint discovery is still waiting on the oneshot channel, so this sentinel is sent to fail it: the server closed or idled the stream before disclosing where JSON-RPC messages should be posted.

Solutions

  1. Verify the server implements the legacy SSE protocol and sends an endpoint event
  2. Check whether a proxy or load balancer is terminating the SSE stream early
  3. Use streamable-http transport instead if supported
Defensive patterns

Strategy: fallback

When it happens

Trigger: Thrown at src-rust/crates/mcp/src/rmcp_backend.rs:317 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of Kuberwastaken/claurst@b0637c97ec (2026-09-10). Data as JSON: /api/errors/b0e619806f47ffed. Report an issue: GitHub.

Appendix: source

Thrown at src-rust/crates/mcp/src/rmcp_backend.rs:317

                }

                if data.trim().is_empty() {
                    return Ok(());
                }

                let message = parse_server_message(&server_name, data)?;
                let _ = incoming_tx.send(message);
                Ok(())
            })
            .await;

            if let Err(e) = result {
                tracing::warn!(server = %server_name, error = %e, "Legacy SSE stream closed with error");
                if let Some(tx) = lock_recover(&endpoint_tx_for_task).take() {
                    let _ = tx.send(Err(anyhow::anyhow!(e.to_string())));
                }
            } else if let Some(tx) = lock_recover(&endpoint_tx_for_task).take() {
                let _ = tx.send(Err(anyhow::anyhow!(
                    "legacy SSE stream closed before announcing endpoint"
                )));
            }
        });
        lock_recover(&self.background_tasks).push(task);

        let endpoint = tokio::time::timeout(std::time::Duration::from_secs(10), endpoint_rx)
            .await
            .map_err(|_| {
                anyhow::anyhow!(
                    "MCP server '{}': timed out waiting for legacy SSE endpoint event",
                    self.server_name
                )
            })?
            .map_err(|_| {
                anyhow::anyhow!(
                    "MCP server '{}': legacy SSE stream ended before endpoint discovery",
                    self.server_name

View on GitHub (pinned to b0637c97ec)