thedotmack/claude-mem · warning · Error
SSE stream response has no body
Error message
SSE stream response has no body
What it means
Thrown by the OpenClaw SSE client after the worker returns HTTP 2xx for /stream but response.body is null/undefined. fetch() in modern runtimes always provides a ReadableStream body for text/event-stream responses, so a missing body implies a stripped-down fetch polyfill, a non-streaming host environment, or an edge runtime that materialised a Response without a body. Like error [0], it is caught at index.ts:602 and triggers a backoff-and-reconnect cycle, so it is self-healing.
Source
Thrown at openclaw/src/index.ts:554
let backoffMs = 1000;
const maxBackoffMs = 30000;
while (!abortController.signal.aborted) {
try {
setConnectionState("reconnecting");
api.logger.info(`[claude-mem] Connecting to SSE stream at ${workerBaseUrl(port)}/stream`);
const response = await fetch(`${workerBaseUrl(port)}/stream`, {
signal: abortController.signal,
headers: { Accept: "text/event-stream" },
});
if (!response.ok) {
throw new Error(`SSE stream returned HTTP ${response.status}`);
}
if (!response.body) {
throw new Error("SSE stream response has no body");
}
setConnectionState("connected");
backoffMs = 1000;
api.logger.info("[claude-mem] Connected to SSE stream");
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = "";
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
if (buffer.length > MAX_SSE_BUFFER_SIZE) {
api.logger.warn("[claude-mem] SSE buffer overflow, clearing buffer");View on GitHub (pinned to d768ba3643)
Solutions
- Run under Node >= 18 or a runtime with full Web Streams fetch support (the worker itself requires Bun; the plugin client should target a modern runtime too).
- Hit /stream directly with curl --no-buffer and confirm bytes flow incrementally, ruling out a proxy buffering the body.
- Check the worker route returns a genuine streaming response (sets Content-Type: text/event-stream and writes chunks with flush), not a closed response.
- If this fires from a test harness, ensure the fetch stub returns { ok:true, body: new ReadableStream(...) }.
- Rely on the existing backoff reconnect — it will succeed once a runtime with a real streaming body is available.
Example fix
// before
const response = await fetch(`${workerBaseUrl(port)}/stream`, { ... });
if (!response.ok) throw new Error(`SSE stream returned HTTP ${response.status}`);
if (!response.body) throw new Error("SSE stream response has no body");
// guard additionally with a clear log distinguishing the runtime cause:
if (!response.body) {
api.logger.warn(`[claude-mem] /stream returned 2xx with no body — fetch runtime may lack streaming support`);
throw new Error("SSE stream response has no body");
} Defensive patterns
Strategy: validation
Validate before calling
// Detect runtimes whose fetch doesn't expose streaming bodies before subscribing:
function fetchHasStreamingBody(): boolean {
// feature-detect: undici/Node>=18 expose ReadableStream on Response
return typeof ReadableStream !== 'undefined';
} Type guard
function responseHasBody(res: Response): res is Response & { body: ReadableStream<Uint8Array> } {
return res.body != null;
} Try / catch
// Already handled by the connect loop's catch (index.ts:602) with backoff.
// If missing bodies are persistent in a given runtime, abort and surface:
if (!response.body) {
api.logger.error('[claude-mem] fetch runtime lacks streaming bodies; disabling SSE feed');
abortController.abort();
} Prevention
- Target Node >= 18 (or Bun) for the plugin host so fetch streams are real.
- Don't put a buffering proxy in front of /stream — forward chunked encoding.
- In tests, stub fetch with { ok:true, body: new ReadableStream({ start(c){ c.enqueue(...); } }) }.
When it happens
Trigger: A host runtime whose fetch returns a Response object with .body undefined (older Node without experimental fetch stream support, or an undici version that does not expose streaming bodies for certain content-types). A worker middleware that sends 200 with an empty/absent body before the SSE handler attaches. A proxy or test harness that buffers the entire response and presents it without a stream handle.
Common situations: Running the OpenClaw plugin under a Node version below 18 (no native fetch streaming) or in an environment that polyfills fetch incompletely. A reverse proxy in front of the worker that does not forward chunked/streamed bodies. Unit tests stubbing fetch with a Response lacking a body field.
Related errors
- SSE stream returned HTTP ${response.status}
- Worker request timed out after ${WORKER_FETCH_TIMEOUT_MS}ms:
- Bun installation completed but binary not found. Please rest
- Failed to install Bun. Please install manually: ${manualInst
- uv installation completed but binary not found. Please resta
AI-assisted analysis of thedotmack/claude-mem@d768ba3643 (2026-08-12).
Data as JSON: /api/errors/ffca0b68389eb67c.
Report an issue: GitHub.