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
- Treat it as session loss: reset state and re-run `initialize` to establish a new session id, then retry the logical operation.
- If 401/403, refresh OAuth credentials before reconnecting.
- Check any proxy in front of the endpoint allows SSE (no buffering, long read timeouts).
- 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
- Re-initialize the session whenever the SSE stream drops rather than reusing the old session id.
- Keep proxies SSE-friendly: disable buffering, extend read timeouts.
- Expect servers to evict idle sessions; re-attach with Last-Event-ID only within a short window.
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
- MCP DELETE failed with {}
- Socket not connected
- mcp.health.opErrorGeneric
- MCP notification {method} failed with {} — {}
- HTTP {} while fetching {} — {}
AI-assisted analysis of tinyhumansai/openhuman@7491200858 (2026-08-17).
Data as JSON: /api/errors/55838a99135e80b3.
Report an issue: GitHub.