can1357/oh-my-pi · error · SSEResumeError
HTTP resuming MCP SSE stream
Error message
HTTP ${response.status} resuming MCP SSE stream: ${text} What it means
While resuming an MCP SSE stream, the server answered the GET with a non-2xx status. The transport reads the response body (error text) and throws SSEResumeError with the status and body, ending the logical stream rather than replaying the original POST.
Solutions
- Check the status and body in the message to identify the cause (404 → session gone, 4xx → bad resume state)
- Reconnect the transport from scratch to establish a new session instead of resuming
- If 404s recur after server restarts, configure the server to persist sessions or shorten client idle gaps
- Verify client and server MCP protocol versions agree on session and Last-Event-ID semantics
Defensive patterns
Strategy: try-catch
Try / catch
transport.onError = (err) => {
if (err instanceof SSEResumeError && /HTTP \d+ resuming MCP SSE stream/.test(err.message)) {
// non-2xx on resume: session likely gone — do a full reconnect
scheduleReconnect();
}
}; Prevention
- Reconnect fully after server restarts; sessions rarely survive them
- Keep the transport active or reconnect before session TTL expiry
- Don't replay in-flight POSTs on resume failure (double-execution risk)
- Read the embedded HTTP status/body to pick reconnect vs. backoff
When it happens
Trigger: The SSE resume GET (Accept: text/event-stream, Last-Event-ID, Mcp-Session-Id) returns any non-ok status — e.g. 404 because the session expired/was evicted, 400 for an unrecognized Last-Event-ID, 5xx server error.
Common situations: Server restarted and lost session state (Mcp-Session-Id no longer valid); session TTL expired during idle; server version upgrade cleared event history; wrong Last-Event-ID format after a server change.
Related errors
- MCP SSE resume returned unsupported Content-Type
- No response received for request ID
- SSE response did not include a body
- Cannot resume session
- Could not discover OAuth endpoints from server response.
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/97d595610aaf7774.
Report an issue: GitHub.
Appendix: source
Thrown at packages/coding-agent/src/mcp/transports/http.ts:363
const generated: Record<string, string> = {
Accept: "text/event-stream",
"Last-Event-ID": resume.lastEventId,
};
if (this.#sessionId) generated["Mcp-Session-Id"] = this.#sessionId;
let response = await this.#fetch({ method: "GET", signal }, generated);
if (this.onAuthError && (response.status === 401 || response.status === 403)) {
await response.body?.cancel();
const newHeaders = await this.onAuthError();
if (!newHeaders) {
throw new SSEResumeError(`HTTP ${response.status} resuming MCP SSE stream: auth refresh failed`);
}
// Persist refreshed headers so subsequent requests use them directly
this.config = { ...this.config, headers: newHeaders };
response = await this.#fetch({ method: "GET", signal }, generated);
}
if (!response.ok) {
const text = await response.text().catch(() => "");
throw new SSEResumeError(`HTTP ${response.status} resuming MCP SSE stream: ${text}`);
}
const contentType = response.headers.get("Content-Type") ?? "";
if (!contentType.includes("text/event-stream") || !response.body) {
await response.body?.cancel();
throw new SSEResumeError(`MCP SSE resume returned unsupported Content-Type: ${contentType || "(missing)"}`);
}
return response;
}
/** Route an SSE message (or batch) to the appropriate handler. */
#dispatchSSEMessage(message: JsonRpcMessage | JsonRpcMessage[]): void {
if (Array.isArray(message)) {
for (const m of message) this.#dispatchSSEMessage(m);
return;
}
// Server-to-client request: has both method and id
if ("method" in message && "id" in message && message.id != null) {
void this.#handleServerRequest(message as JsonRpcRequest);View on GitHub (pinned to 9690622007)