{"record":{"id":"70dc4366b3ef4204","repo":"gitbutlerapp/gitbutler","slug":"failed-to-get-reader-from-response","errorCode":null,"errorMessage":"Failed to get reader from response","messagePattern":"Failed to get reader from response","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"apps/desktop/src/lib/ai/lmStudioClient.ts","lineNumber":76,"sourceCode":"\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\tmodel: this.modelName,\n\t\t\t\t\tmessages: messages,\n\t\t\t\t\ttemperature: DEFAULT_TEMPERATURE,\n\t\t\t\t\tmax_tokens: options?.maxTokens ?? DEFAULT_MAX_TOKENS,\n\t\t\t\t\tstream: shouldStream,\n\t\t\t\t}),\n\t\t\t});\n\n\t\t\tif (!response.ok) {\n\t\t\t\tconst errorText = await response.text();\n\t\t\t\tthrow new Error(`LM Studio API error (${response.status}): ${errorText}`);\n\t\t\t}\n\n\t\t\t// Handle streaming response\n\t\t\tif (shouldStream) {\n\t\t\t\tconst reader = response.body?.getReader();\n\t\t\t\tif (!reader) {\n\t\t\t\t\tthrow new Error(\"Failed to get reader from response\");\n\t\t\t\t}\n\n\t\t\t\tlet result = \"\";\n\t\t\t\tconst decoder = new TextDecoder();\n\n\t\t\t\twhile (true) {\n\t\t\t\t\tconst { done, value } = await reader.read();\n\t\t\t\t\tif (done) break;\n\n\t\t\t\t\tconst chunk = decoder.decode(value, { stream: true });\n\t\t\t\t\tconst lines = chunk\n\t\t\t\t\t\t.split(\"\\n\")\n\t\t\t\t\t\t.filter((line) => line.trim() !== \"\" && line.trim() !== \"data: [DONE]\");\n\n\t\t\t\t\tfor (const line of lines) {\n\t\t\t\t\t\tif (!line.startsWith(\"data:\")) continue;\n\n\t\t\t\t\t\ttry {","sourceCodeStart":58,"sourceCodeEnd":94,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/58e5313667b857ef39a730e380af31816a7b1768/apps/desktop/src/lib/ai/lmStudioClient.ts#L58-L94","documentation":"When streaming is requested, evaluate() reads the response via response.body.getReader(). If the response body stream is unavailable (null body), this error is thrown. It indicates the runtime could not provide a readable stream for the response.","triggerScenarios":"shouldStream is true and response.body is null/undefined — e.g. non-browser fetch implementations (some Node fetch polyfills, certain Tauri/webview contexts) that don't expose response.body for streamed responses.","commonSituations":"Running the client in an environment whose fetch lacks streaming body support; a proxy or server that returns an empty/null body; older polyfilled fetch in tests (jsdom).","solutions":["Disable streaming (omit stream option / set shouldStream=false) so the JSON path is used.","Run in an environment whose fetch supports ReadableStream response bodies.","Update the fetch implementation / polyfill to one supporting streaming."],"exampleFix":"// before\nawait client.evaluate(prompt, { stream: true });\n// after\nawait client.evaluate(prompt, { stream: false });","handlingStrategy":"fallback","validationCode":"if (typeof ReadableStream === 'undefined') {\n  // environment lacks streaming support; use stream: false\n}","typeGuard":null,"tryCatchPattern":"try {\n  return await client.evaluate(prompt, { stream: true });\n} catch (e) {\n  if (e.message === 'Failed to get reader from response') {\n    return await client.evaluate(prompt, { stream: false }); // fallback to non-streaming\n  }\n  throw e;\n}","preventionTips":["Default to stream: false unless you specifically need token streaming.","Test streaming in the actual runtime (Tauri webview/Node), not just the browser.","Avoid fetch polyfills that don't expose response.body."],"tags":["streaming","fetch","environment"],"backgroundTag":"network-request-failed","analyzedSha":"58e5313667b857ef39a730e380af31816a7b1768","analyzedAt":"2026-09-18T06:50:32.052Z","contentChangedAt":"2026-09-18T06:50:32.052Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}