Hmbown/CodeWhale · warning · anyhow

GET timeout

Error message

GET timeout

What it means

During the Streamable HTTP session preflight, the transport opens a GET (SSE) request to establish a session, wrapped in a 5-second `tokio::time::timeout`. The elapsed arm of the select maps to "GET timeout" — the server did not respond to the session-establishing GET within 5 seconds.

Solutions

  1. Retry the connection; transient latency often clears on a second attempt.
  2. Check server health and network latency to the MCP endpoint (curl the URL with a 5s budget).
  3. Reduce server cold-start time or move the MCP endpoint closer to the client; if the server never answers GET preflight, verify it supports Streamable HTTP sessions.

Example fix

// serverless function cold-start
// before: direct connect to cold lambda URL
// after: keep-alive ping or provisioned-concurrency on the MCP endpoint
Defensive patterns

Strategy: retry

Validate before calling

// preflight connectivity check before opening the MCP session
let ok = tokio::time::timeout(
    std::time::Duration::from_secs(5),
    reqwest::get(url),
).await;
if ok.is_err() { eprintln!("endpoint not responding within 5s"); }

Try / catch

match connect_with_policy(...) {
    Err(e) if e.to_string().contains("GET timeout") => {
        // retry with backoff; alert if the endpoint is persistently slow
    }
    other => other?,
}

Prevention

When it happens

Trigger: Connecting to an MCP Streamable HTTP server whose GET preflight request takes longer than 5 seconds — slow or overloaded server, network latency, or a server that never opens the SSE stream for this endpoint.

Common situations: Remote MCP servers behind slow proxies or cold-starting serverless functions; corporate VPN latency; the server does not support the GET preflight and never responds.

Understand the failure class

Background: Request timed out: what client-side request timeouts mean across libraries (Request timed out, TIMED_OUT, APITimeoutError) — this error's family across 39 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/15697e837198673b. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/mcp/http.rs:221

        let headers = tokio::select! {
            biased;
            _ = cancel.cancelled() => {
                anyhow::bail!("MCP session preflight cancelled after plugin authority changed")
            }
            headers = transport.auth.resolved_headers() => headers?,
        };
        let request = apply_safe_custom_headers(
            with_default_mcp_http_headers(transport.client.get(&transport.url), false),
            &headers,
        );
        let response = tokio::select! {
            biased;
            _ = cancel.cancelled() => {
                anyhow::bail!("MCP session preflight cancelled after plugin authority changed")
            }
            response = tokio::time::timeout(Duration::from_secs(5), transport.client.send(request)) => {
                response
                    .map_err(|_| anyhow::anyhow!("GET timeout"))?
                    .map_err(|e| anyhow::anyhow!("GET error: {e}"))?
            }
        };

        // Capture session ID from the GET response so subsequent POSTs
        // (including `initialize`) can include it. This is the same
        // header-reading logic that would be hit inside
        // `StreamableHttpTransport::send` for POST responses, but since
        // the GET is sent before any POST we do it here directly.
        if let Some(sid) = response
            .headers()
            .get("Mcp-Session-Id")
            .and_then(|v| v.to_str().ok())
            && transport.session_id.as_deref() != Some(sid)
        {
            let session_ref = crate::utils::redacted_identifier_for_log(sid);
            tracing::debug!(target: "mcp", session = %session_ref, "captured MCP session ID via GET preflight");
            transport.session_id = Some(sid.to_string());

View on GitHub (pinned to 73e0f67d83)