gemini-cli - ✅(Solved) Fix Gemini 3 `thoughtSignature` silently dropped from chat history [1 pull requests, 1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
google-gemini/gemini-cli#25808Fetched 2026-04-23 07:44:59
View on GitHub
Comments
0
Participants
1
Timeline
4
Reactions
1
Author
Participants
Timeline (top)
referenced ×2cross-referenced ×1labeled ×1

Root Cause

Gemini CLI silently discards thoughtSignature in non functionCall parts on Gemini 3 responses in the in-memory chat history pipeline. Because the loss happens before this.history.push(), every multi-turn conversation is affected from turn 2 onward — checkpoint save/load is just the most visible symptom.

Fix Action

Fixed

PR fix notes

PR #25810: fix(core): retain Gemini 3 thought and thoughtSignature in history

Description (problem / solution / changelog)

Summary

This PR fixes Issue #25808 where Gemini 3 thought parts and thoughtSignatures were being silently dropped from chat history during streaming and part consolidation.

Additionally, it adds model name validation to prevent 404 errors from the API when a typo is provided (related to Issue #5586).

Changes

  • packages/core/src/core/geminiChat.ts:
    • Stop filtering out thought parts in processStreamResponse.
    • Update isValidContent to allow parts with a thoughtSignature even if text is empty.
    • Update consolidation logic to preserve thoughtSignature when merging text parts.
  • packages/core/src/config/models.ts: Added isKnownModel and validateModelName.
  • packages/cli/src/config/config.ts: Integrated validateModelName into CLI config loading.
  • Added comprehensive tests for both fixes.

Fixes #25808.

Changed files

  • packages/cli/src/config/config.ts (modified, +8/-0)
  • packages/cli/src/config/model-validation.test.ts (added, +77/-0)
  • packages/core/src/config/models.ts (modified, +47/-0)
  • packages/core/src/core/geminiChat.ts (modified, +10/-4)
  • packages/core/src/core/thought-retention.test.ts (added, +210/-0)

Code Example

if (!part.thought && part.text !== undefined && part.text === '') {
  return false;   // ← also rejects { text: "", thoughtSignature: "..." }
}

---

if (lastPart?.text && isValidNonThoughtTextPart(lastPart) && isValidNonThoughtTextPart(part)) {
  lastPart.text += part.text;   // ← signature on `part` discarded
}

---

CLI Version    0.38.2
Git Commit     b0ed611a0
Model          Auto (Gemini 3)
Sandbox        no sandbox
OS             linux
Auth Method    Signed in with Google
Tier           Gemini Code Assist in Google One AI Ultra

---

--- a/packages/core/src/core/geminiChat.ts
+++ b/packages/core/src/core/geminiChat.ts
@@ -129,7 +129,7 @@ function isValidContent(content: Content): boolean {
     if (part === undefined || Object.keys(part).length === 0) {
       return false;
     }
-    if (!part.thought && part.text !== undefined && part.text === '') {
+    if (!part.thought && !part.thoughtSignature && part.text === '') {
       return false;
     }
   }
@@ -970,6 +970,9 @@ export class GeminiChat {
         isValidNonThoughtTextPart(part)
       ) {
         lastPart.text += part.text;
+        if (part.thoughtSignature) {
+          lastPart.thoughtSignature = part.thoughtSignature;
+        }
       } else {
         consolidatedParts.push(part);
       }
RAW_BUFFERClick to expand / collapse

What happened?

Gemini CLI silently discards thoughtSignature in non functionCall parts on Gemini 3 responses in the in-memory chat history pipeline. Because the loss happens before this.history.push(), every multi-turn conversation is affected from turn 2 onward — checkpoint save/load is just the most visible symptom.

1. isValidContent rejects the signature-carrier chunk

packages/core/src/core/geminiChat.ts:

if (!part.thought && part.text !== undefined && part.text === '') {
  return false;   // ← also rejects { text: "", thoughtSignature: "..." }
}

The Gemini API docs explicitly describe this pattern (Thought signatures):

"During a model response not containing a FC with a streaming request, the model may return the thought signature in a part with an empty text content part."

So this gate silently drops the carrier chunk before it enters the history pipeline.

2. Consolidation loop drops thoughtSignature on merge

packages/core/src/core/geminiChat.ts, processStreamResponse:

if (lastPart?.text && isValidNonThoughtTextPart(lastPart) && isValidNonThoughtTextPart(part)) {
  lastPart.text += part.text;   // ← signature on `part` discarded
}

Even if (1) is fixed so the carrier enters modelResponseParts, this loop absorbs its empty text into lastPart and discards the signature (isValidNonThoughtTextPart doesn't look at thoughtSignature).

Empirical evidence

Raw stream probe via google-genai SDK against gemini-3-flash-preview, includeThoughts: true, thinkingLevel: HIGH:

  • 34 chunks; signature appears only once — on the final chunk, exactly as the docs describe: { text: "", thoughtSignature_len: 2233, finish_reason: STOP }

After applying the 2-edit fix below in my fork and running a real conversation with /chat save:

  • Before: { "role": "model", "parts": [{ "text": "<reply>" }] } — no signature anywhere
  • After: { "role": "model", "parts": [{ "text": "<reply>", "thoughtSignature": "<~2KB base64>" }] } — a real signature (not the SYNTHETIC_THOUGHT_SIGNATURE placeholder that ensureActiveLoopHasThoughtSignatures injects)

Scope

Loss happens in this.history, not at save time. Affected:

  • TUI chats, non-interactive gemini -p chains, agent loops, ACP sessions (--acp)
  • Every multi-turn turn from the 2nd onward (lossy context re-sent to API)
  • Checkpoint is only the most inspectable symptom — the underlying reasoning-continuity loss is universal

Because the API treats text-part thoughtSignature as OPTIONAL (Recommended), missing signatures don't cause 400 errors → silent quality degradation, not loud failure. This is likely why the bug has persisted.

What did you expect to happen?

thoughtSignature on text parts should be preserved in chat history (and by extension in checkpoints), so subsequent requests can echo them back and the model can resume reasoning state — as recommended by the official Gemini API docs.

Client information

<details> <summary>Client Information</summary>
CLI Version    0.38.2
Git Commit     b0ed611a0
Model          Auto (Gemini 3)
Sandbox        no sandbox
OS             linux
Auth Method    Signed in with Google
Tier           Gemini Code Assist in Google One AI Ultra
</details>

Login information

OAuth (personal Google account).

Anything else we need to know?

Minimal 2-edit fix (on top of v0.38.2):

--- a/packages/core/src/core/geminiChat.ts
+++ b/packages/core/src/core/geminiChat.ts
@@ -129,7 +129,7 @@ function isValidContent(content: Content): boolean {
     if (part === undefined || Object.keys(part).length === 0) {
       return false;
     }
-    if (!part.thought && part.text !== undefined && part.text === '') {
+    if (!part.thought && !part.thoughtSignature && part.text === '') {
       return false;
     }
   }
@@ -970,6 +970,9 @@ export class GeminiChat {
         isValidNonThoughtTextPart(part)
       ) {
         lastPart.text += part.text;
+        if (part.thoughtSignature) {
+          lastPart.thoughtSignature = part.thoughtSignature;
+        }
       } else {
         consolidatedParts.push(part);
       }

extent analysis

TL;DR

The thoughtSignature is silently discarded in non-function call parts of Gemini 3 responses, causing loss of reasoning continuity in multi-turn conversations, and can be fixed by modifying the isValidContent function and the consolidation loop in geminiChat.ts.

Guidance

  • Modify the isValidContent function to check for thoughtSignature in addition to thought, to prevent rejecting parts with empty text but containing a thoughtSignature.
  • Update the consolidation loop to preserve the thoughtSignature when merging parts, by adding a check for part.thoughtSignature and assigning it to lastPart.thoughtSignature if present.
  • Apply the provided 2-edit fix to geminiChat.ts to address the issue.
  • Verify the fix by checking the presence of thoughtSignature in the chat history and checkpoints.

Example

The provided 2-edit fix demonstrates the necessary changes:

--- a/packages/core/src/core/geminiChat.ts
+++ b/packages/core/src/core/geminiChat.ts
@@ -129,7 +129,7 @@ function isValidContent(content: Content): boolean {
     if (part === undefined || Object.keys(part).length === 0) {
       return false;
     }
-    if (!part.thought && part.text !== undefined && part.text === '') {
+    if (!part.thought && !part.thoughtSignature && part.text === '') {
       return false;
     }
   }
@@ -970,6 +970,9 @@ export class GeminiChat {
         isValidNonThoughtTextPart(part)
       ) {
         lastPart.text += part.text;
+        if (part.thoughtSignature) {
+          lastPart.thoughtSignature = part.thoughtSignature;
+        }
       } else {
         consolidatedParts.push(part);
       }

Notes

The fix assumes that the thoughtSignature is only present in parts with empty text, as described in the Gemini API documentation. If this assumption is incorrect, additional modifications may be necessary.

Recommendation

Apply the provided 2-edit fix to geminiChat.ts to address the issue, as it directly addresses the root cause of the problem and has been empirically verified to resolve the issue.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

gemini-cli - ✅(Solved) Fix Gemini 3 `thoughtSignature` silently dropped from chat history [1 pull requests, 1 participants]