mastra-ai/mastra · error

Response body is null

Error message

Response body is null

What it means

After a successful (response.ok) request to /background-tasks/stream, the client checks that a body exists to pipe through the SSE JSON transform. A 2xx response with a null body (possible in some runtimes/polyfills, or with certain proxies that strip bodies) triggers this throw.

Source

Thrown at client-sdks/client-js/src/client.ts:2358

   * Opens an SSE stream of background task events (completed/failed).
   * Returns a Response that can be consumed as a ReadableStream.
   */
  public async streamBackgroundTasks(params: StreamBackgroundTasksParams = {}) {
    const searchParams = new URLSearchParams();
    if (params.agentId) searchParams.set('agentId', params.agentId);
    if (params.runId) searchParams.set('runId', params.runId);
    if (params.threadId) searchParams.set('threadId', params.threadId);
    if (params.resourceId) searchParams.set('resourceId', params.resourceId);
    if (params.taskId) searchParams.set('taskId', params.taskId);
    const qs = searchParams.toString();
    const response: Response = await this.request(`/background-tasks/stream${qs ? `?${qs}` : ''}`, { stream: true });

    if (!response.ok) {
      throw new Error(`Failed to stream background tasks: ${response.statusText}`);
    }

    if (!response.body) {
      throw new Error('Response body is null');
    }

    return response.body.pipeThrough(createSseJsonTransform());
  }

  /**
   * Lists schedules — agent schedules and workflow schedules — with optional
   * filtering by agentId, workflowId, or status. Agent schedules can
   * additionally be filtered by threadId, resourceId, or name.
   */
  public listSchedules(params: ListSchedulesParams = {}): Promise<ListSchedulesResponse> {
    const searchParams = new URLSearchParams();
    if (params.agentId) searchParams.set('agentId', params.agentId);
    if (params.workflowId) searchParams.set('workflowId', params.workflowId);
    if (params.status) searchParams.set('status', params.status);
    if (params.threadId) searchParams.set('threadId', params.threadId);
    if (params.resourceId) searchParams.set('resourceId', params.resourceId);
    if (params.name) searchParams.set('name', params.name);

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Run on a runtime with native streaming fetch support (Node 18+, modern browsers).
  2. Remove or fix fetch polyfills/interceptors that consume or drop the response body.
  3. In tests, mock the response with a real ReadableStream body instead of a bare Response.

Example fix

// before (test mock)
global.fetch = vi.fn().mockResolvedValue(new Response(null, { status: 200 }));
// after
global.fetch = vi.fn().mockResolvedValue(new Response(new ReadableStream({ start(c) { c.enqueue(encoder.encode('data: {}\n\n')); c.close(); } }), { status: 200 }));
Defensive patterns

Strategy: type-guard

Type guard

function hasBody(res: Response): res is Response & { body: ReadableStream<Uint8Array> } {
  return res.body !== null;
}

Try / catch

try {
  const stream = await client.streamBackgroundTasks(params);
} catch (e) {
  if (e instanceof Error && e.message === 'Response body is null') {
    // runtime lacks streaming fetch; fall back to polling background tasks
  }
  throw e;
}

Prevention

When it happens

Trigger: The stream request returned ok but response.body is null — typically when running in a non-DOM runtime without streaming support, using a fetch polyfill, or an interceptor/proxy consuming the body.

Common situations: Node < 18 or environments lacking native streaming fetch; react-native fetch polyfills; a global fetch mock in tests that returns a Response without a body.

Related errors


AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30). Data as JSON: /api/errors/35a7c639ab86cd9b. Report an issue: GitHub.