tinyhumansai/openhuman · warning · anyhow::Error

MCP events GET {} — {}

Error message

MCP events GET {} — {}

What it means

The Streamable-HTTP transport's SSE recovery path fetched the event-stream endpoint (`GET` with `Accept: text/event-stream`, plus `Mcp-Session-Id` and `Last-Event-ID` headers when known) and got a non-2xx status; the status code and response body are embedded. This endpoint is only hit to (re)attach to server-pushed events, so a failure here usually means the session or stream resumption was rejected.

Source

Thrown at src/openhuman/mcp/http_client/client.rs:432

    ) -> anyhow::Result<Vec<McpSseEvent>> {
        self.initialize().await?;
        let protocol_version = self.state.lock().negotiated_protocol_version.clone();
        let session_id = self.state.lock().session_id.clone();
        let mut request = self
            .apply_auth(self.http.get(&self.endpoint), false)
            .header(ACCEPT, "text/event-stream")
            .header(HEADER_PROTOCOL_VERSION, protocol_version);
        if let Some(session_id) = session_id {
            request = request.header(HEADER_SESSION_ID, session_id);
        }
        if let Some(last_event_id) = last_event_id {
            request = request.header("Last-Event-ID", last_event_id);
        }
        let response = request.send().await?;
        let status = response.status();
        let text = response.text().await?;
        if !status.is_success() {
            anyhow::bail!("MCP events GET {} — {}", status.as_u16(), text);
        }
        parse_sse_events(&text)
    }

    pub async fn close_session(&self) -> anyhow::Result<()> {
        let session_id = self.state.lock().session_id.clone();
        let Some(session_id) = session_id else {
            return Ok(());
        };
        let response = self
            .http
            .delete(&self.endpoint)
            .header(HEADER_SESSION_ID, session_id)
            .send()
            .await?;
        if !(response.status().is_success()
            || response.status() == reqwest::StatusCode::METHOD_NOT_ALLOWED)
        {

View on GitHub (pinned to 7491200858)

Solutions

  1. Treat it as session loss: reset state and re-run `initialize` to establish a new session id, then retry the logical operation.
  2. If 401/403, refresh OAuth credentials before reconnecting.
  3. Check any proxy in front of the endpoint allows SSE (no buffering, long read timeouts).
  4. If the server restarts often, move to per-request re-initialization rather than long-lived sessions.
Defensive patterns

Strategy: retry

Try / catch

match client.get_events(/* ... */).await {
    Err(e) if e.to_string().starts_with("MCP events GET 4") => {
        client.reset_session();
        client.initialize().await?; // fresh session, then retry attach
    }
    Err(e) if e.to_string().contains(" 50") => { /* transient: backoff and retry */ }
    other => other,
}

Prevention

When it happens

Trigger: Reconnecting an SSE stream after the server expired/deleted the session (404 with `Mcp-Session-Id` no longer valid); 401 when the OAuth token expired between calls; 4xx/5xx from a gateway in front of the MCP endpoint; `Last-Event-ID` the server no longer retains.

Common situations: Long-lived sessions behind load balancers that drop idle connections and evict session state; auth token expiring during a long tool chain; reverse proxy rejecting the `text/event-stream` accept header; server restart losing in-memory sessions.

Related errors


AI-assisted analysis of tinyhumansai/openhuman@7491200858 (2026-08-17). Data as JSON: /api/errors/55838a99135e80b3. Report an issue: GitHub.