can1357/oh-my-pi · error · MCPTransportError
No response received for request ID ${expectedId}
Error message
No response received for request ID ${expectedId} What it means
The transport POSTed a JSON-RPC request, the server accepted it and opened an SSE stream, but the stream ended (EOF) without ever delivering a JSON-RPC response matching the expected request id, and no event id was supplied so the stream cannot be resumed. The transport deliberately does not replay the POST because the request may already have executed a state-changing tool on the server; it throws a non-retryable receive-stage error instead.
Source
Thrown at packages/coding-agent/src/mcp/transports/http.ts:616
}
}
} catch (error) {
// An abrupt drop (socket reset, body-read failure) is as
// resumable as a server-initiated close once an event ID
// exists; the request timeout still bounds the total wait.
if (captured) return;
if (signal.aborted || resume.lastEventId === null) throw error;
logger.debug("MCP SSE response stream dropped; resuming", {
url: this.config.url,
error: error instanceof Error ? error.message : String(error),
});
}
if (captured) return;
if (signal.aborted) {
throw signal.reason ?? new DOMException("MCP SSE response aborted", "AbortError");
}
if (resume.lastEventId === null) {
throw new MCPTransportError({
transport: "http",
stage: "receive",
failure: "eof",
message: `No response received for request ID ${expectedId}`,
retryable: false,
traceId,
});
}
current = await this.#fetchSSEResume(resume, signal);
}
} catch (error) {
if (captured) return;
// The server accepted this POST (it returned a 2xx SSE stream) before
// the drain or a resume GET failed, so the originating request must
// never be replayed — it may already have executed a state-changing
// tool. Preserve SSEResumeError so #requestWithAuthRetry's no-replay
// guard still fires instead of refreshing auth and re-POSTing, and
// force every other post-acceptance failure non-retryable so theView on GitHub (pinned to 9690622007)
Solutions
- Check the MCP server logs around the trace id for a crash or unhandled error in the request handler and fix the handler to always emit a JSON-RPC response.
- Verify whether the side effect (tool call) actually executed; if the operation is idempotent, re-issue the request explicitly.
- Increase server-side request timeouts so the server does not close the stream before the handler completes.
- Fix proxies to keep streaming connections alive (proxy_read_timeout, disable idle connection reaping).
- Update the MCP server — every accepted request must produce exactly one response message on the SSE stream.
Example fix
// server handler (before): early return skips sending the JSON-RPC reply
if (!resource) return;
sendResult(id, result);
// after: always answer the pending request
if (!resource) { sendError(id, { code: -32602, message: 'not found' }); return; }
sendResult(id, result); Defensive patterns
Strategy: try-catch
Validate before calling
// no pre-call check can detect a dropped response; monitor stream health instead
const ok = await fetch(url, { method: 'OPTIONS' }).then(r => r.ok).catch(() => false);
if (!ok) throw new Error('MCP endpoint unreachable — skip request'); Type guard
function isMissingResponseError(e: unknown): e is MCPTransportError {
return e instanceof MCPTransportError && e.failure === 'eof' && e.stage === 'receive' &&
/No response received for request ID/.test(e.message);
} Try / catch
try {
result = await client.request('tools/call', { name, args });
} catch (e) {
if (isMissingResponseError(e)) {
// request MAY have executed; check server state before re-running
result = await confirmOrReplayIdempotent(name, args);
} else throw e;
} Prevention
- Design tools to be idempotent where possible so unknown-outcome requests can be safely re-issued.
- Set server-side timeouts longer than the client's.
- Alert on MCP server handler exceptions so responses are never silently dropped.
- Keep proxies from severing slow SSE streams (raise read timeouts).
When it happens
Trigger: Server closes the SSE stream after processing the tool but before writing the response; server crashes mid-request; stream interrupted on first physical connection with no SSE id: field ever sent; handler silently fails without sending a reply.
Common situations: MCP server bugs where an exception in the tool handler drops the response; server timeouts shorter than the client's; network cut on the first event frame; servers behind proxies that sever idle streams before the response is emitted.
Related errors
- MCP request failed: ${response.status} ${response.statusText
- HTTP ${response.status} resuming MCP SSE stream: ${text}
- MCP SSE resume returned unsupported Content-Type: ${contentT
- HTTP ${response.status}: ${text}${suffix}
- SSE response did not include a body
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/afe324c7615e4971.
Report an issue: GitHub.