janhq/jan · error

Response body is null

Error message

Response body is null

What it means

After a successful HTTP status on the streaming path, the extension asserts response.body exists before reading the stream; a null body (possible in non-Node fetch implementations or edge runtimes) triggers this error. It indicates the fetch implementation cannot provide a readable stream.

Solutions

  1. Run on a runtime whose fetch supports streaming response bodies (Node >= 18 with undici, current Electron)
  2. Remove fetch polyfills that don't implement ReadableStream bodies
  3. Check that no proxy strips or buffers the response body
  4. Fall back to non-streaming chat if the runtime can't stream

Example fix

// before
const res = await chat(messages, { stream: true })
// after
const res = await chat(messages, { stream: supportsStreamingBody() ? true : false })
Defensive patterns

Strategy: fallback

Validate before calling

function supportsStreamingBody() {
  try {
    return typeof Response !== 'undefined' && new Response().body instanceof ReadableStream
  } catch { return false }
}
const useStream = supportsStreamingBody()

Type guard

const hasBody = (res) => res.body != null && typeof res.body.getReader === 'function'

Try / catch

try {
  return await chat(messages, { stream: true })
} catch (e) {
  if (String(e.message) === 'Response body is null') {
    return chat(messages, { stream: false }) // non-streaming fallback
  }
  throw e
}

Prevention

When it happens

Trigger: Streaming chat where the runtime's fetch returns ok:true but body === null — certain polyfills, undici misconfiguration, or runtimes without streaming body support.

Common situations: Running the extension in a runtime with a limited fetch (older Electron, workers polyfill), or a response intercepted/degraded by a proxy that dropped the body.

Related errors


AI-assisted analysis of janhq/jan@7205d770c1 (2026-09-17). Data as JSON: /api/errors/9bfdfde7654bc8df. Report an issue: GitHub.

Appendix: source

Thrown at extensions/mlx-extension/src/index.ts:486

        )
      }
    }
    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 7205d770c1)