Skip to content

InvalidStreamError retry gated to Gemini 2 models — Gemini 3.x empty responses silently dropped in non-interactive mode #24290

@michalszelagsonos

Description

@michalszelagsonos

What happened?

When using Gemini CLI v0.34.0 in non-interactive mode (--output-format json) with gemini-3.1-pro-preview, the CLI occasionally produces "response": "" — a valid JSON output with an empty response string. The CLI exits with code 0, reports 0 errors in stats, and no review/text is produced. This breaks CI/CD pipelines (GitHub Actions) that depend on the response content.

Root cause trace:

  1. The model returns only thought tokens (150) with no visible text parts in the final turn (after tool results are sent back). getResponseText() in partUtils.ts:96 filters out part.thought parts, returning empty string.

  2. processStreamResponse() in geminiChat.ts correctly detects this and throws InvalidStreamError('Model stream ended with empty response text.', 'NO_RESPONSE_TEXT').

  3. The retry does not fire. In geminiChat.ts line 427, the retry condition is:

    (isContentError && isGemini2Model(model)) || (isRetryable && !signal.aborted)

    isGemini2Model('gemini-3.1-pro-preview') returns false (only matches /^gemini-2/), and InvalidStreamError is not a network error so isRetryable is also false. The error is re-thrown without retry.

  4. turn.ts:368 catches the error and yields GeminiEventType.InvalidStream.

  5. client.ts:792 sees isInvalidStream = true but its "continue on failed API call" retry is also gated by isGemini2Model (line 825). No retry.

  6. nonInteractiveCli.ts has no handler for GeminiEventType.InvalidStream in its event loop (lines 318-394). The event is silently consumed. The loop exits with responseText = '', outputting {"response": ""}.

What did you expect to happen?

The CLI should retry when the model returns an empty response (only thought tokens, no text), regardless of model family. The isGemini2Model guard on InvalidStreamError retry should be removed or expanded to include Gemini 3.x models.

Additionally, nonInteractiveCli.ts should handle the InvalidStream event — at minimum logging a warning to stderr, ideally triggering a retry or returning a non-zero exit code.

Suggested fix

In geminiChat.ts line 427, remove the isGemini2Model guard for content errors:

// Before:
(isContentError && isGemini2Model(model)) || (isRetryable && !signal.aborted)
// After:
isContentError || (isRetryable && !signal.aborted)

Same change in client.ts line 825.

The isGemini2Model guard was likely added when only Gemini 2 models existed. The retry on empty response is safe for any model — it's the same InvalidStreamError mechanism that already works for Gemini 2.

Client information

CLI Version: 0.34.0
Model: gemini-3.1-pro-preview
OS: linux (GitHub Actions runner)
Auth Method: API Key
Mode: non-interactive (--output-format json --yolo)

Reproduction

Run in non-interactive mode with a complex prompt that causes tool calls followed by a response turn. The issue is intermittent — the model occasionally returns only thought tokens without generating visible text.

gemini --yolo --prompt "..." --output-format json 2>stderr.log 1>stdout.log
# Check: jq -r '.response' stdout.log  → empty string
# Check: jq '.stats.models[].tokens.thoughts' stdout.log  → non-zero
# Check: jq '.stats.models[].api.totalErrors' stdout.log  → 0

Affected code paths

File Lines Issue
packages/core/src/core/geminiChat.ts 427 isGemini2Model gates retry for InvalidStreamError
packages/core/src/core/client.ts 825 isGemini2Model gates ContinueOnFailedApiCall retry
packages/cli/src/nonInteractiveCli.ts 318-394 No handler for GeminiEventType.InvalidStream
packages/core/src/config/models.ts 348-352 isGemini2Model only matches gemini-2.*

Login information

API Key

Anything else we need to know?

This also affects v0.35.2 (latest release) — the same isGemini2Model gating exists on HEAD.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/agentIssues related to Core Agent, Tools, Memory, Sub-Agents, Hooks, Agent Qualitystatus/need-triageIssues that need to be triaged by the triage automation.status/possible-duplicate

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions