{"record":{"id":"9bfdfde7654bc8df","repo":"janhq/jan","slug":"response-body-is-null-9bfdfd","errorCode":null,"errorMessage":"Response body is null","messagePattern":"Response body is null","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"extensions/mlx-extension/src/index.ts","lineNumber":486,"sourceCode":"        )\n      }\n    }\n    const response = await fetch(url, {\n      method: 'POST',\n      headers,\n      body,\n      signal: combinedController.signal,\n    }).finally(() => clearTimeout(timeoutId))\n\n    if (!response.ok) {\n      const errorData = await response.json().catch(() => null)\n      throw new Error(\n        `MLX API request failed with status ${response.status}: ${JSON.stringify(errorData)}`\n      )\n    }\n\n    if (!response.body) {\n      throw new Error('Response body is null')\n    }\n\n    const reader = response.body.getReader()\n    const decoder = new TextDecoder('utf-8')\n    let buffer = ''\n\n    try {\n      while (true) {\n        const { done, value } = await reader.read()\n        if (done) break\n\n        buffer += decoder.decode(value, { stream: true })\n\n        const lines = buffer.split('\\n')\n        buffer = lines.pop() || ''\n\n        for (const line of lines) {\n          const trimmedLine = line.trim()","sourceCodeStart":468,"sourceCodeEnd":504,"githubUrl":"https://github.com/janhq/jan/blob/7205d770c1e097c3daf35a911176410e93bc5564/extensions/mlx-extension/src/index.ts#L468-L504","documentation":"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.","triggerScenarios":"Streaming chat where the runtime's fetch returns ok:true but body === null — certain polyfills, undici misconfiguration, or runtimes without streaming body support.","commonSituations":"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.","solutions":["Run on a runtime whose fetch supports streaming response bodies (Node >= 18 with undici, current Electron)","Remove fetch polyfills that don't implement ReadableStream bodies","Check that no proxy strips or buffers the response body","Fall back to non-streaming chat if the runtime can't stream"],"exampleFix":"// before\nconst res = await chat(messages, { stream: true })\n// after\nconst res = await chat(messages, { stream: supportsStreamingBody() ? true : false })","handlingStrategy":"fallback","validationCode":"function supportsStreamingBody() {\n  try {\n    return typeof Response !== 'undefined' && new Response().body instanceof ReadableStream\n  } catch { return false }\n}\nconst useStream = supportsStreamingBody()","typeGuard":"const hasBody = (res) => res.body != null && typeof res.body.getReader === 'function'","tryCatchPattern":"try {\n  return await chat(messages, { stream: true })\n} catch (e) {\n  if (String(e.message) === 'Response body is null') {\n    return chat(messages, { stream: false }) // non-streaming fallback\n  }\n  throw e\n}","preventionTips":["Avoid fetch polyfills lacking ReadableStream body support","Run on Node >= 18 or current Electron with native fetch","Test streaming once at app startup to pick stream vs non-stream mode","Ensure proxies don't buffer/drop streaming responses"],"tags":["mlx","streaming","fetch","null-body"],"backgroundTag":"empty-response-body","analyzedSha":"7205d770c1e097c3daf35a911176410e93bc5564","analyzedAt":"2026-09-17T14:27:30.100Z","contentChangedAt":"2026-09-17T14:27:30.100Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}