litellm - 💡(How to fix) Fix [Bug]: Invalid Thought Signature when switching Gemini between Vertex AI and Gemini [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
BerriAI/litellm#24571Fetched 2026-04-08 01:32:40
View on GitHub
Comments
0
Participants
1
Timeline
3
Reactions
0
Author
Participants
Timeline (top)
labeled ×3

Code Example

import openai

# open ai client
client = openai.OpenAI(
    api_key='...',
    base_url='...'
)

# test tool
tools=[{
    "type": "function",
    "function": {
        "name": "test_tool",
        "description": "A simple test tool",
        "parameters": {
            "type": "object",
            "properties": {}
        }
    }
}]

# messages
messages=[{
    "role": "user",
    "content": "Call the 'test_tool' again and again, do not stop!"
}]

# models
models = [
    "vertex_ai/gemini-3.1-pro-preview",
    "gemini/gemini-3.1-pro-preview"
]

# iterate between models
model_idx = 0

# start loop
while True:

    # model for this loop
    model = models[model_idx]

    # use other one next time
    if model_idx == 0: model_idx = 1
    else: model_idx = 0

    # debug
    print(f"CALLING: {model}")

    # send request
    response = client.chat.completions.create(
        model=model,
        tools=tools,
        messages=messages
    )

    # OPTION 1: append message only
    # this always fails immediately when calling Vertex AI after Gemini
    # and occasionally when calling Gemini after Vertex AI
    # it works fine when only using one of the two
    messages.append(response.choices[0].message)

    # OPTION 2: append full choice
    # this seems to work when switching between the two - why?
    #messages.append(response.choices[0])

    # tool calls made by llm
    tool_calls = response.choices[0].message.tool_calls

    if not tool_calls or len(tool_calls) == 0:
        # tell model to not stop
        messages.append({
            "role": "user",
            "content": "You MUST NOT stop calling the 'test_tool'!"
        })
    else:
        # append tool call response
        messages.append({
            "tool_call_id": tool_calls[0].id,
            "role": "tool",
            "name": tool_calls[0].function.name,
            "content": "Success. Now, you MUST call the 'test_tool' again!"
        })

---
RAW_BUFFERClick to expand / collapse

Check for existing issues

  • I have searched the existing issues and checked that my issue is not a duplicate.

What happened?

We are using Gemini 3.1 Pro Preview in multi-turn conversations with tool calls on both, Vertex AI and Gemini. We are using these providers as fallbacks for each other.

Unfortunately, we are regularly getting HTTP 400 errors with invalid thought signature when switching between them within the same conversation.

It seems to work better if we send back the whole Choice instead of just the ChatCompletionMessage. Is this a bug or requirement?

Steps to Reproduce

Use the following Python code with LiteLLM Proxy

import openai

# open ai client
client = openai.OpenAI(
    api_key='...',
    base_url='...'
)

# test tool
tools=[{
    "type": "function",
    "function": {
        "name": "test_tool",
        "description": "A simple test tool",
        "parameters": {
            "type": "object",
            "properties": {}
        }
    }
}]

# messages
messages=[{
    "role": "user",
    "content": "Call the 'test_tool' again and again, do not stop!"
}]

# models
models = [
    "vertex_ai/gemini-3.1-pro-preview",
    "gemini/gemini-3.1-pro-preview"
]

# iterate between models
model_idx = 0

# start loop
while True:

    # model for this loop
    model = models[model_idx]

    # use other one next time
    if model_idx == 0: model_idx = 1
    else: model_idx = 0

    # debug
    print(f"CALLING: {model}")

    # send request
    response = client.chat.completions.create(
        model=model,
        tools=tools,
        messages=messages
    )

    # OPTION 1: append message only
    # this always fails immediately when calling Vertex AI after Gemini
    # and occasionally when calling Gemini after Vertex AI
    # it works fine when only using one of the two
    messages.append(response.choices[0].message)

    # OPTION 2: append full choice
    # this seems to work when switching between the two - why?
    #messages.append(response.choices[0])

    # tool calls made by llm
    tool_calls = response.choices[0].message.tool_calls

    if not tool_calls or len(tool_calls) == 0:
        # tell model to not stop
        messages.append({
            "role": "user",
            "content": "You MUST NOT stop calling the 'test_tool'!"
        })
    else:
        # append tool call response
        messages.append({
            "tool_call_id": tool_calls[0].id,
            "role": "tool",
            "name": tool_calls[0].function.name,
            "content": "Success. Now, you MUST call the 'test_tool' again!"
        })

Relevant log output

What part of LiteLLM is this about?

Proxy

What LiteLLM version are you on ?

v1.81.9

Twitter / LinkedIn details

No response

extent analysis

Fix Plan

To resolve the HTTP 400 errors with invalid thought signature when switching between Vertex AI and Gemini, you should append the full Choice object instead of just the ChatCompletionMessage. This is because the Choice object contains additional metadata that is required for the conversation context.

Here are the steps to fix the issue:

  • Modify the code to append the full Choice object:
messages.append(response.choices[0])

Instead of:

messages.append(response.choices[0].message)
  • Verify that the tool_calls list is not empty before accessing its elements:
if response.choices[0].message.tool_calls and len(response.choices[0].message.tool_calls) > 0:
    tool_calls = response.choices[0].message.tool_calls
else:
    tool_calls = []
  • Update the code to handle the tool_calls list correctly:
if not tool_calls or len(tool_calls) == 0:
    # tell model to not stop
    messages.append({
        "role": "user",
        "content": "You MUST NOT stop calling the 'test_tool'!"
    })
else:
    # append tool call response
    messages.append({
        "tool_call_id": tool_calls[0].id,
        "role": "tool",
        "name": tool_calls[0].function.name,
        "content": "Success. Now, you MUST call the 'test_tool' again!"
    })

Verification

To verify that the fix worked, run the modified code and check for any HTTP 400 errors. The conversation should now switch between Vertex AI and Gemini without any issues.

Extra Tips

  • Make sure to handle any exceptions that may occur during the conversation, such as network errors or invalid responses from the AI models.
  • Consider adding logging to track the conversation flow and any errors that may occur.
  • If you continue to experience issues, try updating the LiteLLM Proxy to the latest version or checking the Vertex AI and Gemini documentation for any specific requirements or limitations.

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