gitbutlerapp/gitbutler · error

Failed to get reader from response

Error message

Failed to get reader from response

What it means

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.

Solutions

  1. Disable streaming (omit stream option / set shouldStream=false) so the JSON path is used.
  2. Run in an environment whose fetch supports ReadableStream response bodies.
  3. Update the fetch implementation / polyfill to one supporting streaming.

Example fix

// before
await client.evaluate(prompt, { stream: true });
// after
await client.evaluate(prompt, { stream: false });
Defensive patterns

Strategy: fallback

Validate before calling

if (typeof ReadableStream === 'undefined') {
  // environment lacks streaming support; use stream: false
}

Try / catch

try {
  return await client.evaluate(prompt, { stream: true });
} catch (e) {
  if (e.message === 'Failed to get reader from response') {
    return await client.evaluate(prompt, { stream: false }); // fallback to non-streaming
  }
  throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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).

Related errors


AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18). Data as JSON: /api/errors/70dc4366b3ef4204. Report an issue: GitHub.

Appendix: source

Thrown at apps/desktop/src/lib/ai/lmStudioClient.ts:76

				body: JSON.stringify({
					model: this.modelName,
					messages: messages,
					temperature: DEFAULT_TEMPERATURE,
					max_tokens: options?.maxTokens ?? DEFAULT_MAX_TOKENS,
					stream: shouldStream,
				}),
			});

			if (!response.ok) {
				const errorText = await response.text();
				throw new Error(`LM Studio API error (${response.status}): ${errorText}`);
			}

			// Handle streaming response
			if (shouldStream) {
				const reader = response.body?.getReader();
				if (!reader) {
					throw new Error("Failed to get reader from response");
				}

				let result = "";
				const decoder = new TextDecoder();

				while (true) {
					const { done, value } = await reader.read();
					if (done) break;

					const chunk = decoder.decode(value, { stream: true });
					const lines = chunk
						.split("\n")
						.filter((line) => line.trim() !== "" && line.trim() !== "data: [DONE]");

					for (const line of lines) {
						if (!line.startsWith("data:")) continue;

						try {

View on GitHub (pinned to 58e5313667)