fix(core): use snake_case thought_signature for Vertex AI compatibility#26652
fix(core): use snake_case thought_signature for Vertex AI compatibility#26652QuentinTorg wants to merge 10 commits intogoogle-gemini:mainfrom
Conversation
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request addresses a 400 INVALID_ARGUMENT error encountered when using the Vertex AI backend. The issue stemmed from a property naming mismatch where the backend strictly expects Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. Footnotes
|
|
Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA). View this failed invocation of the CLA check for more information. For the most up to date status, view the checks section at the bottom of the pull request. |
There was a problem hiding this comment.
Code Review
This pull request renames the thoughtSignature property to thought_signature within the GeminiChat class and its associated tests to ensure compatibility with Vertex AI. Feedback suggests explicitly removing the camelCase thoughtSignature property when injecting the snake_case version to avoid potential validation errors from the API.
| if (!part.thought_signature) { | ||
| newParts[j] = { | ||
| ...part, | ||
| thoughtSignature: SYNTHETIC_THOUGHT_SIGNATURE, | ||
| // @ts-expect-error We are injecting a property that the SDK doesn't officially support yet. | ||
| thought_signature: SYNTHETIC_THOUGHT_SIGNATURE, | ||
| }; |
There was a problem hiding this comment.
When injecting thought_signature for Vertex AI compatibility, it is important to ensure that the camelCase thoughtSignature property is removed if it exists. Vertex AI's loop validation is strict about property naming, and providing both keys in the payload might still result in a 400 INVALID_ARGUMENT error. Destructuring the part to exclude the camelCase key before spreading it into the new object ensures only the required snake_case property is sent to the API, which aligns with the repository's practice of using spread syntax for clean object construction.
if (!part.thought_signature) {
const { thoughtSignature, ...rest } = part;
newParts[j] = {
...rest,
// @ts-expect-error We are injecting a property that the SDK doesn't officially support yet.
thought_signature: SYNTHETIC_THOUGHT_SIGNATURE,
};References
- When constructing an object from multiple data sources, consistently use a merge operation (e.g., spread syntax
{...a, ...b}) instead of assignment to avoid accidental overwrites and order-dependent logic.
|
I have signed the CLA |
Addresses multiple issues in GeminiChat when migrating to snake_case thought_signature: 1. Prevents overwriting of legacy thoughtSignature when parsing active loops. 2. Prevents data loss for cached/legacy interactions when context management is disabled. 3. Maps existing camelCase signatures to snake_case. 4. Replaces @ts-expect-error with strict inline type intersections for better type safety.
…into fix/thought-signature-vertex-400
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request migrates the thoughtSignature property to snake_case (thought_signature) within the GeminiChat class and its tests, adding logic to map legacy camelCase signatures to the new format. A review comment points out that the history scrubber in historyHardening.ts also needs to be updated to prevent the new snake_case property from being stripped, which is necessary to avoid API errors.
| const p = part as typeof part & { | ||
| thought_signature?: string; | ||
| thoughtSignature?: string; | ||
| }; |
There was a problem hiding this comment.
The PR description mentions that packages/core/src/utils/historyHardening.ts also needs to be updated because its scrubber currently drops the snake_case version of the signature. If the scrubber removes thought_signature from previous turns in the history, multi-turn tool loops may still encounter 400 INVALID_ARGUMENT errors in subsequent requests, as the API requires these signatures for reasoning continuity across the entire active loop. This change should be included to ensure the fix is robust.
Addresses code review comment by updating the history scrubber to properly preserve thought_signature, preventing Vertex AI validation errors in subsequent multi-turn requests. Also maps legacy thoughtSignature to the new format.
…into fix/thought-signature-vertex-400
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request transitions the thoughtSignature property to snake_case (thought_signature) within the core package to ensure compatibility with Vertex AI. The changes update the GeminiChat class and history hardening utilities to map legacy camelCase properties to the new format, supported by new unit tests. Feedback suggests refining the property mapping logic in the history hardening utility to trim whitespace and utilize nullish coalescing for improved robustness.
| if (part.thoughtSignature) { | ||
| scrubbed['thought_signature'] = part.thoughtSignature; | ||
| } else if (part.thought_signature) { | ||
| scrubbed['thought_signature'] = part.thought_signature; | ||
| } |
There was a problem hiding this comment.
When mapping legacy thoughtSignature to thought_signature, ensure the value is trimmed to prevent whitespace-only strings from being accepted, consistent with repository standards for optional strings. Use nullish coalescing to handle the optional properties defined in the interface.
const sig = part.thoughtSignature?.trim() ?? part.thought_signature?.trim();
if (sig) {
scrubbed['thought_signature'] = sig;
}References
- When using an optional string with a fallback value, trim the optional string and use the fallback if the result is empty to avoid uninformative messages from whitespace-only strings.
- When consuming an object, if a property is optional in its type definition, callers must handle the undefined case (e.g., by providing a default with ??).
Summary
Fixes the API 400 INVALID_ARGUMENT error occurring specifically with Vertex AI backend. The error was caused by a mismatch in property naming conventions: the SDK uses camelCase (
thoughtSignature), but the Vertex AI loopvalidation strictly requires snake_case (
thought_signature).Details
The Gemini CLI uses a scrubber (
historyHardening.ts) to clean history before sending to the API. However, the functionensureActiveLoopHasThoughtSignaturesruns after the scrubber and was injecting the camelCase key tobypass loop detection limits. Because it ran post-scrubber, the API received the invalid key.
This PR updates
ensureActiveLoopHasThoughtSignaturesto injectthought_signatureinstead. Since the official Node.js SDK typesPartas havingthoughtSignature, we suppress the TypeScript error locally. This resolvesthe 400 errors without reverting the broader architectural improvements made in recent releases.
Research Findings (2026-05-08)
thoughtSignature(accepted by some early preview endpoints via SDK mapping).thought_signature(mandatory for Gemini 2.5/3 reasoning models).@google/genaiv2.0.0 SDK (late 2025/early 2026).rolefor tool responses has transitioned from"function"to"user". While"function"is still currently tolerated in some 1.x SDK paths, the 2.x specification expects"user". Weshould monitor this for future
400errors.Note: Further investigation shows that
packages/core/src/utils/historyHardening.tsalso needs to be updated to support and preserve this key, as it currently injects the camelCase version and its scrubber drops thesnake_case version.
Related Issues
Closes #26639
How to Validate
npm run build400 INVALID_ARGUMENTerror for a missingthought_signature.npm test -w @google/gemini-cli-core -- src/core/geminiChat.test.tsPre-Merge Checklist