vercel/ai · error · Error

The response body is empty.

Error message

The response body is empty.

What it means

In HttpChatTransport.reconnectToStream (packages/ai/src/ui/http-chat-transport.ts:265), the reconnect HTTP response returned a 200-ish status but its body is null. The transport needs a readable stream to feed into processResponseStream, so it throws when `response.body` is falsy. This typically means the server/env stripped or failed to produce the body despite an OK status.

Source

Thrown at packages/ai/src/ui/http-chat-transport.ts:265

      method: 'GET',
      headers,
      credentials,
      signal: options.abortSignal,
    });

    // no active stream found, so we do not resume
    if (response.status === 204) {
      return null;
    }

    if (!response.ok) {
      throw new Error(
        (await response.text()) ?? 'Failed to fetch the chat response.',
      );
    }

    if (!response.body) {
      throw new Error('The response body is empty.');
    }

    return this.processResponseStream(response.body);
  }

  protected abstract processResponseStream(
    stream: ReadableStream<Uint8Array<ArrayBufferLike>>,
  ): ReadableStream<UIMessageChunk>;
}

View on GitHub (pinned to 69428b1f8b)

Solutions

  1. Fix the server reconnect endpoint so it returns a UI message stream body (e.g. `result.toUIMessageStreamResponse()` from streamText), not an empty 200.
  2. Check for proxies/middleware that may strip or buffer response bodies on the reconnect route.
  3. Verify your fetch mock/polyfill in tests provides a real ReadableStream body.
  4. Wrap reconnect/resume in try-catch and fall back to loading prior messages without streaming when the body is empty.

Example fix

// before (server route)
return new Response(null, { status: 200 });
// after
const result = streamText({ model, messages });
return result.toUIMessageStreamResponse({ originalMessages: messages });
Defensive patterns

Strategy: fallback

Validate before calling

const res = await fetch(reconnectUrl);
if (!res.body) {
  // fall back to loading persisted messages instead of streaming
}

Type guard

function hasResponseBody(res: Response): res is Response & { body: ReadableStream } {
  return res.body != null;
}

Try / catch

try {
  await chat.reconnectToStream({ chatId });
} catch (e) {
  if (e instanceof Error && e.message === 'The response body is empty.') {
    await loadPersistedMessages(chatId); // fallback path
  } else throw e;
}

Prevention

When it happens

Trigger: Calling chat reconnect (useChat resume / Chat.reconnectToStream) where the server responds with an OK status code but the fetch Response has `body === null` (e.g. empty-bodied response, opaque responses, or runtimes like some edge environments returning null bodies).

Common situations: Custom chat route returning 200 without a stream body; reconnect endpoint implemented incorrectly (returns plain `new Response()` or an empty body); proxy/CDN stripping response bodies; unit-test mocks that build a Response without a body.

Related errors


AI-assisted analysis of vercel/ai@69428b1f8b (2026-08-30). Data as JSON: /api/errors/9ba581aa9040b69f. Report an issue: GitHub.