mastra-ai/mastra · error · Error
Response body is null
Error message
Response body is null
What it means
Thrown by Run.stream() when the workflow stream request succeeded (response.ok) but response.body is null, so there is no readable stream to pipe through the chunk transform. Indicates an empty or body-less response despite a success status.
Source
Thrown at client-sdks/client-js/src/resources/run.ts:244
method: 'POST',
body: {
inputData: params.inputData,
initialState: params.initialState,
requestContext,
tracingOptions: params.tracingOptions,
resourceId: params.resourceId,
perStep: params.perStep,
closeOnSuspend: params.closeOnSuspend,
},
stream: true,
});
if (!response.ok) {
throw new Error(`Failed to stream workflow: ${response.statusText}`);
}
if (!response.body) {
throw new Error('Response body is null');
}
// Pipe the response body through the transform stream
return response.body.pipeThrough(this.createChunkTransformStream());
}
/**
* Observe (reconnect to) an existing workflow stream.
* Use this to resume receiving events after a disconnection.
*
* @param params.offset - Optional position to resume from (0-based). If omitted, replays all events.
* @returns Promise containing a ReadableStream of workflow events
*
* @example
* ```typescript
* // Reconnect to a workflow stream from a specific position
* const stream = await run.observe({ offset: 42 });
*View on GitHub (pinned to 75dd419e61)
Solutions
- Disable response buffering for streaming endpoints on proxies/gateways.
- Confirm server actually emits SSE data for the workflow run (curl -N).
- Use a spec-compliant fetch (Node 18+ native fetch) in your environment/tests.
- Retry the stream request if transient.
Example fix
// before
const stream = await run.stream({ inputData });
for await (const chunk of stream) { ... }
// after
try {
const stream = await run.stream({ inputData });
for await (const chunk of stream) { ... }
} catch (e) {
if (e.message.includes('Response body is null')) console.error('Empty stream — check proxy/SSE config');
else throw e;
} Defensive patterns
Strategy: fallback
Validate before calling
// before streaming, confirm infra supports SSE
const res = await fetch(`${baseUrl}/health`);
if (!res.body) throw new Error('Fetch runtime does not expose response.body'); Type guard
function hasStreamBody(res: Response): res is Response & { body: ReadableStream } { return res.body !== null; } Try / catch
try {
const stream = await run.stream({ inputData });
for await (const chunk of stream) handle(chunk);
} catch (e) {
if (/Response body is null/.test(e.message)) {
await pollRunStatus(run); // fallback path
} else throw e;
} Prevention
- Disable SSE buffering on proxies/load balancers
- Test streaming paths with real HTTP, not body-less mocks
- Use Node 18+ native fetch
- Implement polling fallback for critical workflow monitoring
When it happens
Trigger: run.stream() returning 200 but with no body — proxies stripping the SSE payload, server aborting after headers, or a fetch implementation (mock/polyfill) without body support (client-sdks/client-js/src/resources/run.ts:244).
Common situations: Gateway buffering/terminating SSE connections; undici/fetch polyfills in unit tests; server killed mid-response.
Related errors
- Failed to stream workflow: ${response.statusText}
- Failed to observe workflow stream: ${response.statusText}
- Failed to stream vNext workflow: ${response.statusText}
- Failed to time travel workflow: ${response.statusText}
- Failed to stream background tasks: ${response.statusText}
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/97b486fa802f799d.
Report an issue: GitHub.