abhigyanpatwari/GitNexus · error · Error

LLM returned empty response

Error message

LLM returned empty response

What it means

Non-streaming success path: the provider returned HTTP 200 with valid JSON, but json.choices[0].message.content is missing or falsy. GitNexus refuses to return an empty completion because downstream wiki rendering would emit blank pages. Note this checks only .content; providers that put text in reasoning_content or in a different envelope will trip it.

Source

Thrown at gitnexus/src/core/wiki/llm-client.ts:456

        `Azure content filter blocked this request. The prompt triggered content policy. Details: ${errorText.slice(0, 300)}`,
      );
    }

    // Any other non-OK response here is a terminal 4xx — resilientFetch
    // already retried 5xx/429 to exhaustion and would have thrown above.
    throw new Error(`LLM API error (${response.status}): ${errorText.slice(0, 500)}`);
  }

  // Streaming path
  if (useStream && response.body) {
    return await readSSEStream(response.body, options!.onChunk!);
  }

  // Non-streaming path
  const json = (await response.json()) as any;
  const choice = json.choices?.[0];
  if (!choice?.message?.content) {
    throw new Error('LLM returned empty response');
  }

  return {
    content: choice.message.content,
    promptTokens: json.usage?.prompt_tokens,
    completionTokens: json.usage?.completion_tokens,
  };
}

/**
 * Read an SSE stream from an OpenAI-compatible streaming response.
 */
async function readSSEStream(
  body: ReadableStream<Uint8Array>,
  onChunk: (charsReceived: number) => void,
): Promise<LLMResponse> {
  const decoder = new TextDecoder();
  const reader = body.getReader();

View on GitHub (pinned to d540b00184)

Solutions

  1. Raise config.maxTokens / max_completion_tokens so the model has room to emit text.
  2. If using a reasoning model, ensure it is detected (isReasoningModel) or pass provider correctly so the response envelope matches.
  3. If Azure null-content due to content filter, sanitize the prompt or relax the filter (see error 224).
  4. Switch to streaming (pass onChunk in options) which parses SSE chunks and surfaces partial content.
  5. Verify the server is actually OpenAI-compatible (choices[].message.content); if it uses a different shape, use a different baseUrl/provider.

Example fix

// before
await callLLM(prompt, { baseUrl, apiKey, model: 'o3-mini', maxTokens: 1 });
// -> LLM returned empty response

// after
await callLLM(prompt, { baseUrl, apiKey, model: 'o3-mini', maxTokens: 4096 });
// or stream:
await callLLM(prompt, { baseUrl, apiKey, model }, undefined, { onChunk: n => progress(n) });
Defensive patterns

Strategy: validation

Validate before calling

function looksReasoning(model) { return /^o[1-9]\d*/i.test(model); }
// give reasoning models enough tokens and parse via streaming for partial content
config.maxTokens = Math.max(config.maxTokens ?? 0, 1024);
if (looksReasoning(config.model)) config.isReasoningModel = true;

Type guard

function isEmptyResponseError(e) {
  return e instanceof Error && /LLM returned empty response/.test(e.message);
}

Try / catch

try { return await callLLM(prompt, config); }
catch (e) {
  if (isEmptyResponseError(e)) {
    // retry with streaming + larger budget, or non-reasoning fallback
    return await callLLM(prompt, { ...config, maxTokens: 4096 }, undefined, { onChunk: () => {} });
  }
  throw e;
}

Prevention

When it happens

Trigger: Provider returns 200 but choices[0].message.content is null/'' (some Azure deployments return null content when content_filter triggered without a 400; some OpenAI-compatible servers return empty for safety models; reasoning models that put everything in reasoning_content with empty primary content). Also: max_completion_tokens set so low the model emitted nothing.

Common situations: Reasoning model (o1/o3) parsed via non-reasoning path; Azure content filter returning 200 with null content; tiny max_completion_tokens budget; broken OpenAI-compatible server returning malformed choices array; tool-calling-format response with no text.

Related errors


AI-assisted analysis of abhigyanpatwari/GitNexus@d540b00184 (2026-08-12). Data as JSON: /api/errors/1dc49cd226b62c16. Report an issue: GitHub.