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:
-
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.
-
processStreamResponse() in geminiChat.ts correctly detects this and throws InvalidStreamError('Model stream ended with empty response text.', 'NO_RESPONSE_TEXT').
-
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.
-
turn.ts:368 catches the error and yields GeminiEventType.InvalidStream.
-
client.ts:792 sees isInvalidStream = true but its "continue on failed API call" retry is also gated by isGemini2Model (line 825). No retry.
-
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.
What happened?
When using Gemini CLI v0.34.0 in non-interactive mode (
--output-format json) withgemini-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:
The model returns only thought tokens (150) with no visible text parts in the final turn (after tool results are sent back).
getResponseText()inpartUtils.ts:96filters outpart.thoughtparts, returning empty string.processStreamResponse()ingeminiChat.tscorrectly detects this and throwsInvalidStreamError('Model stream ended with empty response text.', 'NO_RESPONSE_TEXT').The retry does not fire. In
geminiChat.tsline 427, the retry condition is:isGemini2Model('gemini-3.1-pro-preview')returnsfalse(only matches/^gemini-2/), andInvalidStreamErroris not a network error soisRetryableis also false. The error is re-thrown without retry.turn.ts:368catches the error and yieldsGeminiEventType.InvalidStream.client.ts:792seesisInvalidStream = truebut its "continue on failed API call" retry is also gated byisGemini2Model(line 825). No retry.nonInteractiveCli.tshas no handler forGeminiEventType.InvalidStreamin its event loop (lines 318-394). The event is silently consumed. The loop exits withresponseText = '', 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
isGemini2Modelguard onInvalidStreamErrorretry should be removed or expanded to include Gemini 3.x models.Additionally,
nonInteractiveCli.tsshould handle theInvalidStreamevent — at minimum logging a warning to stderr, ideally triggering a retry or returning a non-zero exit code.Suggested fix
In
geminiChat.tsline 427, remove theisGemini2Modelguard for content errors:Same change in
client.tsline 825.The
isGemini2Modelguard was likely added when only Gemini 2 models existed. The retry on empty response is safe for any model — it's the sameInvalidStreamErrormechanism that already works for Gemini 2.Client information
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.
Affected code paths
packages/core/src/core/geminiChat.tsisGemini2Modelgates retry forInvalidStreamErrorpackages/core/src/core/client.tsisGemini2ModelgatesContinueOnFailedApiCallretrypackages/cli/src/nonInteractiveCli.tsGeminiEventType.InvalidStreampackages/core/src/config/models.tsisGemini2Modelonly matchesgemini-2.*Login information
API Key
Anything else we need to know?
This also affects v0.35.2 (latest release) — the same
isGemini2Modelgating exists on HEAD.