can1357/oh-my-pi · error · Error
Legacy SSE response did not include a body
Error message
Legacy SSE response did not include a body
What it means
The legacy SSE transport's connect() received a 2xx response but with a null body, so there is no event stream to read the 'endpoint' event from. A valid legacy SSE handshake requires a readable stream whose first event names the POST message endpoint; without a body the transport cannot complete initialization and throws. This runs after the !response.ok check, so the server did return success — just with nothing in it.
Source
Thrown at packages/coding-agent/src/mcp/transports/sse.ts:90
const connection = new AbortController();
const timeout = resolveMCPTimeoutMs(this.#config.timeout);
const operation = createMCPTimeout(timeout, connection.signal);
const endpointReady = Promise.withResolvers<void>();
this.#sseConnection = connection;
try {
const response = await this.#fetch(
this.#config.url,
{ method: "GET", signal: operation.signal },
{ Accept: "text/event-stream" },
);
if (!response.ok) {
const text = await response.text();
throw new Error(`HTTP ${response.status}: ${text}`);
}
if (!response.body) {
throw new Error("Legacy SSE response did not include a body");
}
void this.#readSSEStream(response.body, operation, endpointReady).finally(() => {
const wasConnected = this.#connected;
if (this.#sseConnection === connection) this.#sseConnection = null;
if (wasConnected) this.onClose?.();
});
await endpointReady.promise;
} catch (error) {
operation.clear();
if (this.#sseConnection === connection) this.#sseConnection = null;
connection.abort();
if (operation.isTimeoutAbort(error)) {
throw new Error(`Legacy SSE endpoint timeout after ${timeout}ms`);
}
throw error;
}
}View on GitHub (pinned to 9690622007)
Solutions
- Confirm with curl -N -H 'Accept: text/event-stream' <url> whether the server actually streams an 'endpoint' event or closes immediately.
- Disable proxy response buffering for the SSE route (e.g. proxy_buffering off in nginx).
- If the server runs on a platform without streaming support (some serverless runtimes), move it to a streaming-capable host.
- Update or fix the MCP server so a 200 SSE response always carries the stream.
- Migrate to the Streamable HTTP transport, which this client prefers and most current servers support.
Example fix
// server (before)
res.writeHead(200, { 'Content-Type': 'text/event-stream' });
res.end();
// after: send the required endpoint event and keep alive
res.writeHead(200, { 'Content-Type': 'text/event-stream' });
res.write(`event: endpoint\ndata: ${messageEndpointUrl}\n\n`); Defensive patterns
Strategy: validation
Validate before calling
const res = await fetch(sseUrl, { headers: { Accept: 'text/event-stream' } });
if (res.ok && !res.body) throw new Error('Server returns 200 SSE with empty body — cannot run legacy SSE here');
await res.body?.cancel(); Type guard
function hasStreamingBody(res: Response): res is Response & { body: ReadableStream<Uint8Array> } {
return res.body !== null;
} Try / catch
try {
const transport = await createSseTransport(config);
} catch (e) {
if (e instanceof Error && e.message.includes('did not include a body')) {
logger.error('Legacy SSE server/proxy returns empty streams; switch transport');
return useStreamableHttp(config);
}
throw e;
} Prevention
- Verify with curl -N that the endpoint streams an endpoint event before configuring it.
- Disable proxy buffering on the SSE route.
- Host streaming MCP servers on runtimes that support streaming responses.
- Prefer the Streamable HTTP transport on modern servers.
When it happens
Trigger: Server (or intermediary) returns 200 with Content-Type: text/event-stream but an empty/immediately-closed body; a proxy that buffers and truncates the stream; server handler that sets SSE headers then ends the response.
Common situations: Misconfigured nginx/gateway buffering an SSE stream into an empty body; buggy or outdated MCP server SSE implementations; serverless platforms that don't support streaming responses hosting the MCP server.
Related errors
- SSE response did not include a body
- HTTP ${response.status}: ${text}
- Legacy SSE endpoint timeout after ${timeout}ms
- Legacy SSE endpoint origin mismatch: expected ${configuredUr
- SSE stream ended without a resumable event ID
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/2d64e1eaa6cdc52c.
Report an issue: GitHub.