google-gemini/gemini-cli · error · InvalidStreamError

NO_RESPONSE_TEXT

NO_RESPONSE_TEXT

Error message

Model stream ended with empty response text.

What it means

The stream ended with a benign finishReason (typically STOP) but produced zero visible output text: no tool call was made, and after geminiChat.ts:1488-1497 strips zero-width/invisible characters and HTML comment blocks the remaining text is empty, with no reasoning thoughts (a thoughts-only response would have thrown THINKING_ONLY_RESPONSE first) and no blocked-finishReason variant (MAX_TOKENS/SAFETY/RECITATION/OTHER each have their own error). Tool-response turns (isOriginalFunctionResponse=true) are exempt, so this only fires on genuine user turns. Like all InvalidStreamErrors it is retried internally with a nudge before being surfaced.

Source

Thrown at packages/core/src/core/geminiChat.ts:1626

          throw new InvalidStreamError(
            'Model stream ended due to recitation settings (RECITATION) with empty response text.',
            'RECITATION_BLOCKED',
          );
        }
        if (finishReason === FinishReason.OTHER) {
          throw new InvalidStreamError(
            'Model stream ended due to other settings (OTHER) with empty response text.',
            'OTHER_BLOCKED',
          );
        }
        if (hasThoughts) {
          throw new InvalidStreamError(
            'Model stream ended with empty response text but contained reasoning thoughts.',
            'THINKING_ONLY_RESPONSE',
          );
        }
        if (!isOriginalFunctionResponse) {
          throw new InvalidStreamError(
            'Model stream ended with empty response text.',
            'NO_RESPONSE_TEXT',
          );
        }
      }
    }

    // Flush buffered thoughts from the successful attempt
    for (const thought of bufferedThoughts) {
      this.chatRecordingService.recordThought(thought);
    }

    // Flush buffered usage metadata and token counts from the successful attempt
    if (bufferedUsageMetadata) {
      this.chatRecordingService.recordMessageTokens(bufferedUsageMetadata);
      if (bufferedUsageMetadata.promptTokenCount !== undefined) {
        this.lastPromptTokenCount = bufferedUsageMetadata.promptTokenCount;
      }

View on GitHub (pinned to 3c311beac2)

Solutions

  1. Retry the turn with backoff — most occurrences are one-off model flakiness; the library already retried 3 times with a nudge, so a single outer retry after a pause usually clears it.
  2. Loosen output-format instructions in the prompt: forbid empty replies ('always include at least one sentence of content'), and do not ask the model to wrap its entire answer in HTML comments or emit format-only scaffolding.
  3. If it reproduces on one prompt, minimize that prompt and test directly against the Gemini API to confirm the model itself returns empty content; then restructure the prompt or switch model version.
  4. Check generationConfig (adequate maxOutputTokens, sensible temperature) and, if you suspect quiet refusals, review safetySettings for the request.

Example fix

// before: format-only instructions that can yield an empty visible reply
const system = 'Respond ONLY with an HTML comment containing the plan. No other text.';

// after: same machine-readable goal, empty output forbidden
const system = 'Write the plan inside an HTML comment. Before the comment, write one short sentence summarizing the plan. Never send an empty reply.';

// plus an outer safety net:
async function turnWithNudge(chat, msg) {
  try {
    return await collect(chat.sendMessageStream(msg));
  } catch (e) {
    if (e instanceof InvalidStreamError && e.type === 'NO_RESPONSE_TEXT') {
      return collect(chat.sendMessageStream('Your last reply was empty. Please answer again with actual content.'));
    }
    throw e;
  }
}
Defensive patterns

Strategy: retry

Type guard

import { InvalidStreamError } from './packages/core/src/core/geminiChat.js';

function isNoResponseTextError(e: unknown): e is InvalidStreamError & { type: 'NO_RESPONSE_TEXT' } {
  return e instanceof InvalidStreamError && e.type === 'NO_RESPONSE_TEXT';
}

Try / catch

try {
  for await (const chunk of chat.sendMessageStream(userMsg)) { handle(chunk); }
} catch (e) {
  if (isNoResponseTextError(e)) {
    // Library already retried with a nudge; resend the turn once with an explicit anti-empty instruction.
    return runTurn(chat, userMsg, { extraInstruction: 'Never reply with empty or comment-only content.' });
  }
  throw e;
}

Prevention

When it happens

Trigger: A sendMessageStream turn whose consolidated parts contain only whitespace, zero-width characters, or HTML comment blocks (all stripped at geminiChat.ts:1488-1497), or no text parts at all, while the model still reports finishReason STOP. Commonly caused by prompts that force a narrow output format (e.g., 'reply only inside <!-- ... --> comments', 'output XML only, no prose'), models that intermittently emit a bare STOP with empty candidates, or gateways that forward an empty final chunk.

Common situations: System prompts demanding machine-only formats (XML-only, comment-wrapped) that push the model to emit format scaffolding with no payload; temperature=0 loops that repeat the same empty completion; silent refusals on borderline prompts that return STOP instead of SAFETY; a mock/proxy test harness that yields a chunk with finishReason STOP but empty parts; observed intermittently under heavy load — most occurrences are transient model flakiness.

Related errors


AI-assisted analysis of google-gemini/gemini-cli@3c311beac2 (2026-08-21). Data as JSON: /api/errors/1c6ac0312980aad8. Report an issue: GitHub.