unslothai/unsloth · error · Error
Stream response missing body
Error message
Stream response missing body
What it means
Thrown when the /v1/chat/completions response is ok (2xx) but response.body is null, so there is no ReadableStream to read SSE events from. This guards the invariant that a streaming completions call must return a body. It fires before any chunk is parsed, so no partial transcript exists.
Source
Thrown at studio/frontend/src/features/chat/api/chat-api.ts:1317
export async function* streamChatCompletions(
payload: OpenAIChatCompletionsRequest,
signal: AbortSignal,
): AsyncGenerator<OpenAIChatChunk> {
const response = await authFetch("/v1/chat/completions", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload),
signal,
});
if (!response.ok) {
const body = await response.json().catch(() => null);
throw new Error(parseErrorText(response.status, body));
}
if (!response.body) {
throw new Error("Stream response missing body");
}
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = "";
let completed = false;
// EOF without `[DONE]` or a finish_reason chunk means the stream was cut mid-generation.
let sawTerminalSignal = false;
let terminalFinishReason: string | null = null;
let sawAssistantContent = false;
let sawReasoningContent = false;
const throwIfReasoningOnlyLength = () => {
if (
terminalFinishReason === "length" &&
sawReasoningContent &&
!sawAssistantContent
) {View on GitHub (pinned to 203007d190)
Solutions
- Check whether a proxy, service worker, or fetch shim sits between the app and the inference endpoint and strips response bodies.
- Verify the server actually sets Content-Type: text/event-stream and streams a body for this request.
- In tests, mock the Response with a real ReadableStream body.
Example fix
// before (broken test mock)
const res = new Response('', { status: 200 });
// after
const res = new Response(sseEncoder.encode('data: [DONE]\n\n'), { status: 200 }); Defensive patterns
Strategy: validation
Validate before calling
if (!response.body) {
// detect stripped streams early: check content-type before reading
const ct = response.headers.get('content-type') ?? '';
if (!ct.includes('event-stream')) console.warn('non-SSE content-type:', ct);
} Try / catch
try { yield* readStream(); } catch (e) { if (e.message === 'Stream response missing body') hintAtProxyIssue(); throw e; } Prevention
- Ensure streaming endpoints bypass buffering proxies (gzip off, chunked allowed).
- In tests, always construct Response with a real ReadableStream.
- Register service workers with a pass-through for /v1/chat/completions.
When it happens
Trigger: A 2xx response with no body: an intermediary (proxy, service worker, mock fetch) stripping the stream; a server bug returning Content-Length: 0 with 200; a fetch polyfill or test double that omits body.
Common situations: Corporate proxies or middleware that buffer/empty streaming responses; service workers intercepting fetch; unit tests mocking fetch with new Response() lacking a body; HTTP/1.0 intermediaries that drop chunked encoding.
Related errors
- {path}: not valid JSON: {exc}
- ChatGPT returned a malformed stream.
- ChatGPT stream ended before completion.
- Response interrupted: the connection dropped before the mode
- ${parsed.error.message || "Stream error"}
AI-assisted analysis of unslothai/unsloth@203007d190 (2026-08-15).
Data as JSON: /api/errors/b964e5dd77dc3a4c.
Report an issue: GitHub.