Skip to content

Conversation

@fkesheh
Copy link
Contributor

@fkesheh fkesheh commented Jan 28, 2026

Problem

Two related issues when using Gemini 3 with thinking mode:

1. thought_signature Error on Retry

When retrying a conversation that had tool calls, Gemini rejects with:

"function call `todo_write` in the 7. content block is missing a `thought_signature`"

Root Cause: Gemini 3 with thinking mode requires thought_signature to be passed back with tool calls. We don't store these signatures in the database, so old messages cause this error.

2. Duplicate Messages on Preemptive Timeout

When preemptive timeout triggers, users see both:

  • ✅ "I had to stop due to the time limit" (correct)
  • ❌ "network error" with Retry button (incorrect)

Root Cause: abortController.abort() triggers both the graceful finish reason AND a network error in the useChat hook.

Solution

Fix 1: Strip reasoning for Gemini (lib/utils/message-processor.ts, lib/api/chat-handler.ts)

  • Added stripReasoningFromMessagesForGemini() function
  • Removes reasoning and thinking parts from assistant messages with tool calls
  • Only applies to Gemini models (detected by modelId.includes("gemini"))
  • Safe because reasoning is regenerated on each request

Fix 2: Hide error on graceful timeout (app/components/Messages.tsx)

  • Hide MessageErrorState when finishReason === "timeout"
  • Shows only the intended "I had to stop" notice, not the spurious error

Test Plan

  • Retry a Gemini conversation that had tool calls → no thought_signature error
  • Trigger preemptive timeout → only shows "I had to stop" message, no "network error"
  • New conversations with reasoning still work correctly
  • Non-Gemini models unaffected

Files Changed

  • lib/utils/message-processor.ts - Added reasoning stripping functions
  • lib/api/chat-handler.ts - Apply stripping for Gemini models
  • app/components/Messages.tsx - Hide error state on graceful timeout

…ture errors

Gemini 3 with thinking mode requires thought_signature to be passed back
with tool calls. Since we don't store thought signatures, this fix strips
reasoning parts from messages before sending to Gemini models.

This is a safeguard for old messages saved without thought signatures.
@vercel
Copy link

vercel bot commented Jan 28, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
hackerai Ready Ready Preview, Comment Jan 28, 2026 9:06pm

Request Review

@coderabbitai
Copy link

coderabbitai bot commented Jan 28, 2026

📝 Walkthrough

Walkthrough

Adds Gemini-specific helpers to strip assistant "reasoning" parts from messages and applies them in the chat handler before model streaming and after summarization; also conditionally hides error UI for preemptive timeouts in the Messages component.

Changes

Cohort / File(s) Summary
Gemini Reasoning Stripping Utilities
lib/utils/message-processor.ts
Added isReasoningPart and exported stripReasoningForGemini<T> and stripReasoningFromMessagesForGemini<T> to remove reasoning/thinking parts from assistant messages that include tool calls. Helpers are additive and preserve non-reasoning content.
Chat Handler Gemini Integration
lib/api/chat-handler.ts
Imported stripReasoningFromMessagesForGemini, added isGeminiModel detection (checks modelId for "gemini" or "google"), and applies stripping to finalMessages before streaming and to summarizedMessages after summarization when Gemini is detected.
Error UI timeout handling
app/components/Messages.tsx
Adjusted rendering to hide the error UI when an error exists with finishReason === "timeout"; otherwise error UI remains displayed.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐇 I hopped through code with eager paws,

I trimmed the thoughts with gentle claws,
Gemini's chatter now sings clear,
No stray think-threads lingering near,
Hop, patch, and forward we go—cheer! 🎉

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main objectives: fixing Gemini thought_signature errors and addressing duplicate timeout messages. It accurately reflects the primary changes across chat-handler.ts, message-processor.ts, and Messages.tsx.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

When preemptive timeout triggers, the abort causes both:
1. FinishReasonNotice to show "I had to stop due to time limit"
2. MessageErrorState to show "network error"

This fix hides the error state when finishReason is "timeout" since
it's a graceful abort, not a real error.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
lib/utils/message-processor.ts (1)

253-262: Docstring separated from its function.

The new code was inserted between the docstring at lines 253-256 (for completeIncompleteToolCalls) and the function itself at line 306. This breaks the documentation association.

Proposed fix: Move new functions before the docstring

Move isReasoningPart, stripReasoningForGemini, and stripReasoningFromMessagesForGemini above line 253 so the existing docstring remains adjacent to completeIncompleteToolCalls.

+/**
+ * Checks if a part is a reasoning/thinking part.
+ */
+const isReasoningPart = (part: Record<string, any>): boolean => {
+  return part.type === "reasoning" || part.type === "thinking";
+};
+
+/**
+ * Strips reasoning parts from messages for Gemini models.
+ * ...
+ */
+export const stripReasoningForGemini = ...
+
+/**
+ * Strips reasoning parts from all messages for Gemini models.
+ * ...
+ */
+export const stripReasoningFromMessagesForGemini = ...
+
 /**
  * Completes any incomplete tool calls in messages with a timeout result.
  * This prevents "Tool result is missing" errors when resuming after a preemptive timeout.
  */
 export const completeIncompleteToolCalls = ...
🧹 Nitpick comments (1)
lib/api/chat-handler.ts (1)

437-446: Remove the overly broad "google" model check.

The includes("google") check is unnecessary and misleading. The thought_signature requirement is specific to Gemini models, not all Google models. Since the function being called is stripReasoningFromMessagesForGemini, the model detection should reflect this:

Suggested fix
const isGeminiModel =
-  configuredModelId.includes("gemini") ||
-  configuredModelId.includes("google");
+  configuredModelId.includes("gemini");

@fkesheh fkesheh changed the title fix(gemini): strip reasoning from old messages to avoid thought_signature errors fix(gemini): resolve thought_signature errors and duplicate timeout messages Jan 28, 2026
@rossmanko rossmanko merged commit da8f402 into main Jan 28, 2026
4 checks passed
@fkesheh fkesheh deleted the fix/gemini-thought-signature branch January 29, 2026 01:33
@coderabbitai coderabbitai bot mentioned this pull request Feb 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants