janhq/jan · error · Error
Response body is null
Error message
Response body is null
What it means
Thrown by handleStreamingResponse() right after a successful (OK) fetch, when response.body is null. The HTTP layer returned a 2xx but no readable stream body, so SSE parsing cannot proceed. This is a runtime/transport anomaly rather than a server rejection.
Source
Thrown at extensions/mlx-extension/src/index.ts:456
)
}
}
const response = await fetch(url, {
method: 'POST',
headers,
body,
signal: combinedController.signal,
}).finally(() => clearTimeout(timeoutId))
if (!response.ok) {
const errorData = await response.json().catch(() => null)
throw new Error(
`MLX API request failed with status ${response.status}: ${JSON.stringify(errorData)}`
)
}
if (!response.body) {
throw new Error('Response body is null')
}
const reader = response.body.getReader()
const decoder = new TextDecoder('utf-8')
let buffer = ''
try {
while (true) {
const { done, value } = await reader.read()
if (done) break
buffer += decoder.decode(value, { stream: true })
const lines = buffer.split('\n')
buffer = lines.pop() || ''
for (const line of lines) {
const trimmedLine = line.trim()View on GitHub (pinned to fad3f12a14)
Solutions
- Ensure the fetch runs in an environment with full ReadableStream support (not an opaque response).
- Avoid consuming response.body/json before reaching the stream reader.
- If the runtime lacks body, fall back to non-streaming chat and parse the full JSON.
- Check that no proxy intercepts and re-wraps the response without a body.
Example fix
// before
if (!response.body) throw new Error('Response body is null')
const reader = response.body.getReader()
// after
if (!response.body) {
logger.warn('Streaming body null; falling back to non-stream parse')
const data = (await response.json()) as chatCompletion
return (async function* () { yield toChunk(data) })()
}
const reader = response.body.getReader() Defensive patterns
Strategy: fallback
Validate before calling
// verify runtime exposes streaming bodies before requesting stream const supportsBody = typeof Response !== 'undefined' && 'body' in Response.prototype if (!supportsBody) opts.stream = false // request non-streaming instead
Type guard
function hasReadableBody(r: Response): r is Response & { body: ReadableStream<Uint8Array> } {
return r.body != null && typeof (r.body as any).getReader === 'function'
} Try / catch
try {
return yield* engine.chat({ ...opts, stream: true }, abort)
} catch (e) {
if (/Response body is null/.test(String(e))) {
// fall back to non-streaming
const full = (await engine.chat({ ...opts, stream: false }, abort)) as chatCompletion
return full
}
throw e
} Prevention
- Run in an environment with full ReadableStream support.
- Do not consume response.body before the stream reader.
- Provide a non-streaming fallback when body is unavailable.
When it happens
Trigger: An opaque response (type 'opaque'/'opaqueredirect') whose body is null; a fetch polyfill or environment (some WebKit/JSC builds) that does not expose body for streaming; a misconfigured fetch that consumed the body before this check; a response with Content-Length but no body stream.
Common situations: Running under a runtime where Response.body is unsupported; a service worker/proxy returning an empty body; the body was accidentally read elsewhere; CORS opacity stripping the body.
Related errors
- Response body is null
- MLX API request failed with status ${response.status}: ${JSO
- API request failed with status ${response.status}: ${JSON.st
- Tokenize request failed with status ${res.status}
- Unable to find a suitable port for MLX model
AI-assisted analysis of janhq/jan@fad3f12a14 (2026-08-12).
Data as JSON: /api/errors/9bfdfde7654bc8df.
Report an issue: GitHub.