langchain - 💡(How to fix) Fix ChatOpenAI fails to provide thought signatures on multi-turn conversations when used with Gemini models, resulting in 400 BadRequestError

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…

ChatOpenAI fails to provide thought signatures on multi-turn conversations when used with Gemini models, resulting in 400 BadRequestError.

When calling Gemini models, Google returns a thought signature (https://ai.google.dev/gemini-api/docs/thought-signatures). Thought signatures are encrypted representations of the model's internal thought process and are used to preserve reasoning context across multi-step interactions. According to Google's documentation:

As a general rule, if you receive a thought signature in a model response, you should pass it back exactly as received when sending the conversation history in the next turn. When using Gemini 3 models, you must pass back thought signatures during function calling, otherwise you will get a validation error (4xx status code).

When calling Gemini via ChatOpenAI, the thought signature is not passed in followup turns in a multi-turn conversation, thereby triggering a 400 BadRequestError.

Error Message

Turn 1 OK; tool_calls=[{'name': 'greet', 'args': {}, 'id': 'zv7ul4f5', 'type': 'tool_call'}] additional_kwargs keys: ['refusal'] Traceback (most recent call last): File "gemini3_openai_issue/main.py", line 38, in <module> main() ~~~~^^ File "gemini3_openai_issue/main.py", line 33, in main final = llm.invoke(msgs) File "gemini3_openai_issue/.venv-buggy/lib/python3.13/site-packages/langchain_core/runnables/base.py", line 5753, in invoke return self.bound.invoke( ~~~~~~~~~~~~~~~~~^ input, ^^^^^^ self._merge_configs(config), ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ **{**self.kwargs, **kwargs}, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ) ^ File "gemini3_openai_issue/.venv-buggy/lib/python3.13/site-packages/langchain_core/language_models/chat_models.py", line 472, in invoke self.generate_prompt( ~~~~~~~~~~~~~~~~~~~~^ [self._convert_input(input)], ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...<6 lines>... **kwargs, ^^^^^^^^^ ).generations[0][0], ^ File "gemini3_openai_issue/.venv-buggy/lib/python3.13/site-packages/langchain_core/language_models/chat_models.py", line 1752, in generate_prompt return self.generate(prompt_messages, stop=stop, callbacks=callbacks, **kwargs) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "gemini3_openai_issue/.venv-buggy/lib/python3.13/site-packages/langchain_core/language_models/chat_models.py", line 1559, in generate self._generate_with_cache( ~~~~~~~~~~~~~~~~~~~~~~~~~^ m, ^^ ...<2 lines>... **kwargs, ^^^^^^^^^ ) ^ File "gemini3_openai_issue/.venv-buggy/lib/python3.13/site-packages/langchain_core/language_models/chat_models.py", line 1899, in _generate_with_cache result = self._generate( messages, stop=stop, run_manager=run_manager, **kwargs ) File "gemini3_openai_issue/.venv-buggy/lib/python3.13/site-packages/langchain_openai/chat_models/base.py", line 1653, in _generate _handle_openai_bad_request(e) ~~~~~~~~~~~~~~~~~~~~~~~~~~^^^ File "gemini3_openai_issue/.venv-buggy/lib/python3.13/site-packages/langchain_openai/chat_models/base.py", line 1650, in _generate raw_response = self.client.with_raw_response.create(**payload) File "gemini3_openai_issue/.venv-buggy/lib/python3.13/site-packages/openai/_legacy_response.py", line 367, in wrapped return cast(LegacyAPIResponse[R], func(*args, **kwargs)) ~~~~^^^^^^^^^^^^^^^^^ File "gemini3_openai_issue/.venv-buggy/lib/python3.13/site-packages/openai/_utils/_utils.py", line 298, in wrapper return func(*args, **kwargs) File "gemini3_openai_issue/.venv-buggy/lib/python3.13/site-packages/openai/resources/chat/completions/completions.py", line 1215, in create return self._post( ~~~~~~~~~~^ "/chat/completions", ^^^^^^^^^^^^^^^^^^^^ ...<51 lines>... stream_cls=Stream[ChatCompletionChunk], ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ) ^ File "gemini3_openai_issue/.venv-buggy/lib/python3.13/site-packages/openai/_base_client.py", line 1332, in post return cast(ResponseT, self.request(cast_to, opts, stream=stream, stream_cls=stream_cls)) ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "gemini3_openai_issue/.venv-buggy/lib/python3.13/site-packages/openai/_base_client.py", line 1105, in request raise self._make_status_error_from_response(err.response) from None openai.BadRequestError: Error code: 400 - [{'error': {'code': 400, 'message': 'Function call is missing a thought_signature in functionCall parts. This is required for tools to work correctly, and missing thought_signature may lead to degraded model performance. Additional data, function call default_api:greet , position 2. Please refer to https://ai.google.dev/gemini-api/docs/thought-signatures for more details.', 'status': 'INVALID_ARGUMENT'}}]

Root Cause

ChatOpenAI fails to provide thought signatures on multi-turn conversations when used with Gemini models, resulting in 400 BadRequestError.

When calling Gemini models, Google returns a thought signature (https://ai.google.dev/gemini-api/docs/thought-signatures). Thought signatures are encrypted representations of the model's internal thought process and are used to preserve reasoning context across multi-step interactions. According to Google's documentation:

As a general rule, if you receive a thought signature in a model response, you should pass it back exactly as received when sending the conversation history in the next turn. When using Gemini 3 models, you must pass back thought signatures during function calling, otherwise you will get a validation error (4xx status code).

When calling Gemini via ChatOpenAI, the thought signature is not passed in followup turns in a multi-turn conversation, thereby triggering a 400 BadRequestError.

Fix Action

Fix / Workaround

  • This is a bug, not a usage question.
  • I added a clear and descriptive title that summarizes this issue.
  • I used the GitHub search to find a similar question and didn't find it.
  • I am sure that this is a bug in LangChain rather than my code.
  • The bug is not resolved by updating to the latest stable version of LangChain (or the specific integration package).
  • This is not related to the langchain-community package.
  • I posted a self-contained, minimal, reproducible example. A maintainer can copy it and run it AS IS.

Other Dependencies

httpx: 0.28.1 jsonpatch: 1.33 openai: 2.36.0 orjson: 3.11.9 packaging: 26.2 pydantic: 2.13.4 pyyaml: 6.0.3 requests: 2.33.1 requests-toolbelt: 1.0.0 tenacity: 9.1.4 tiktoken: 0.12.0 typing-extensions: 4.15.0 uuid-utils: 0.14.1 xxhash: 3.7.0 zstandard: 0.25.0

Code Example

import os

from dotenv import load_dotenv
from langchain_core.messages import HumanMessage, ToolMessage
from langchain_core.tools import tool
from langchain_openai import ChatOpenAI

load_dotenv()

@tool
def greet() -> str:
    """Say hello."""
    return "Hello!"


llm = ChatOpenAI(
    model="gemini-3-flash-preview",
    base_url="https://generativelanguage.googleapis.com/v1beta/openai/",
    api_key=os.environ["GEMINI_API_KEY"],
    temperature=0,
).bind_tools([greet])


def main() -> None:
    msgs: list = [HumanMessage("Use the greet tool")]
    ai = llm.invoke(msgs)
    print(f"Turn 1 OK; tool_calls={ai.tool_calls}")
    print(f"additional_kwargs keys: {list(ai.additional_kwargs)}")

    tc_id = ai.tool_calls[0]["id"]
    msgs += [ai, ToolMessage(content="Hello!", tool_call_id=tc_id)]
    final = llm.invoke(msgs)
    print(f"Turn 2 OK: {final.content!r}")


if __name__ == "__main__":
    main()

---

Turn 1 OK; tool_calls=[{'name': 'greet', 'args': {}, 'id': 'zv7ul4f5', 'type': 'tool_call'}]
additional_kwargs keys: ['refusal']
Traceback (most recent call last):
  File "gemini3_openai_issue/main.py", line 38, in <module>
    main()
    ~~~~^^
  File "gemini3_openai_issue/main.py", line 33, in main
    final = llm.invoke(msgs)
  File "gemini3_openai_issue/.venv-buggy/lib/python3.13/site-packages/langchain_core/runnables/base.py", line 5753, in invoke
    return self.bound.invoke(
           ~~~~~~~~~~~~~~~~~^
        input,
        ^^^^^^
        self._merge_configs(config),
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        **{**self.kwargs, **kwargs},
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "gemini3_openai_issue/.venv-buggy/lib/python3.13/site-packages/langchain_core/language_models/chat_models.py", line 472, in invoke
    self.generate_prompt(
    ~~~~~~~~~~~~~~~~~~~~^
        [self._convert_input(input)],
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    ...<6 lines>...
        **kwargs,
        ^^^^^^^^^
    ).generations[0][0],
    ^
  File "gemini3_openai_issue/.venv-buggy/lib/python3.13/site-packages/langchain_core/language_models/chat_models.py", line 1752, in generate_prompt
    return self.generate(prompt_messages, stop=stop, callbacks=callbacks, **kwargs)
           ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "gemini3_openai_issue/.venv-buggy/lib/python3.13/site-packages/langchain_core/language_models/chat_models.py", line 1559, in generate
    self._generate_with_cache(
    ~~~~~~~~~~~~~~~~~~~~~~~~~^
        m,
        ^^
    ...<2 lines>...
        **kwargs,
        ^^^^^^^^^
    )
    ^
  File "gemini3_openai_issue/.venv-buggy/lib/python3.13/site-packages/langchain_core/language_models/chat_models.py", line 1899, in _generate_with_cache
    result = self._generate(
        messages, stop=stop, run_manager=run_manager, **kwargs
    )
  File "gemini3_openai_issue/.venv-buggy/lib/python3.13/site-packages/langchain_openai/chat_models/base.py", line 1653, in _generate
    _handle_openai_bad_request(e)
    ~~~~~~~~~~~~~~~~~~~~~~~~~~^^^
  File "gemini3_openai_issue/.venv-buggy/lib/python3.13/site-packages/langchain_openai/chat_models/base.py", line 1650, in _generate
    raw_response = self.client.with_raw_response.create(**payload)
  File "gemini3_openai_issue/.venv-buggy/lib/python3.13/site-packages/openai/_legacy_response.py", line 367, in wrapped
    return cast(LegacyAPIResponse[R], func(*args, **kwargs))
                                      ~~~~^^^^^^^^^^^^^^^^^
  File "gemini3_openai_issue/.venv-buggy/lib/python3.13/site-packages/openai/_utils/_utils.py", line 298, in wrapper
    return func(*args, **kwargs)
  File "gemini3_openai_issue/.venv-buggy/lib/python3.13/site-packages/openai/resources/chat/completions/completions.py", line 1215, in create
    return self._post(
           ~~~~~~~~~~^
        "/chat/completions",
        ^^^^^^^^^^^^^^^^^^^^
    ...<51 lines>...
        stream_cls=Stream[ChatCompletionChunk],
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "gemini3_openai_issue/.venv-buggy/lib/python3.13/site-packages/openai/_base_client.py", line 1332, in post
    return cast(ResponseT, self.request(cast_to, opts, stream=stream, stream_cls=stream_cls))
                           ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "gemini3_openai_issue/.venv-buggy/lib/python3.13/site-packages/openai/_base_client.py", line 1105, in request
    raise self._make_status_error_from_response(err.response) from None
openai.BadRequestError: Error code: 400 - [{'error': {'code': 400, 'message': 'Function call is missing a thought_signature in functionCall parts. This is required for tools to work correctly, and missing thought_signature may lead to degraded model performance. Additional data, function call `default_api:greet` , position 2. Please refer to https://ai.google.dev/gemini-api/docs/thought-signatures for more details.', 'status': 'INVALID_ARGUMENT'}}]
RAW_BUFFERClick to expand / collapse

Submission checklist

  • This is a bug, not a usage question.
  • I added a clear and descriptive title that summarizes this issue.
  • I used the GitHub search to find a similar question and didn't find it.
  • I am sure that this is a bug in LangChain rather than my code.
  • The bug is not resolved by updating to the latest stable version of LangChain (or the specific integration package).
  • This is not related to the langchain-community package.
  • I posted a self-contained, minimal, reproducible example. A maintainer can copy it and run it AS IS.

Package (Required)

  • langchain
  • langchain-openai
  • langchain-anthropic
  • langchain-classic
  • langchain-core
  • langchain-model-profiles
  • langchain-tests
  • langchain-text-splitters
  • langchain-chroma
  • langchain-deepseek
  • langchain-exa
  • langchain-fireworks
  • langchain-groq
  • langchain-huggingface
  • langchain-mistralai
  • langchain-nomic
  • langchain-ollama
  • langchain-openrouter
  • langchain-perplexity
  • langchain-qdrant
  • langchain-xai
  • Other / not sure / general

Related Issues / PRs

No response

Reproduction Steps / Example Code (Python)

import os

from dotenv import load_dotenv
from langchain_core.messages import HumanMessage, ToolMessage
from langchain_core.tools import tool
from langchain_openai import ChatOpenAI

load_dotenv()

@tool
def greet() -> str:
    """Say hello."""
    return "Hello!"


llm = ChatOpenAI(
    model="gemini-3-flash-preview",
    base_url="https://generativelanguage.googleapis.com/v1beta/openai/",
    api_key=os.environ["GEMINI_API_KEY"],
    temperature=0,
).bind_tools([greet])


def main() -> None:
    msgs: list = [HumanMessage("Use the greet tool")]
    ai = llm.invoke(msgs)
    print(f"Turn 1 OK; tool_calls={ai.tool_calls}")
    print(f"additional_kwargs keys: {list(ai.additional_kwargs)}")

    tc_id = ai.tool_calls[0]["id"]
    msgs += [ai, ToolMessage(content="Hello!", tool_call_id=tc_id)]
    final = llm.invoke(msgs)
    print(f"Turn 2 OK: {final.content!r}")


if __name__ == "__main__":
    main()

Error Message and Stack Trace (if applicable)

Turn 1 OK; tool_calls=[{'name': 'greet', 'args': {}, 'id': 'zv7ul4f5', 'type': 'tool_call'}]
additional_kwargs keys: ['refusal']
Traceback (most recent call last):
  File "gemini3_openai_issue/main.py", line 38, in <module>
    main()
    ~~~~^^
  File "gemini3_openai_issue/main.py", line 33, in main
    final = llm.invoke(msgs)
  File "gemini3_openai_issue/.venv-buggy/lib/python3.13/site-packages/langchain_core/runnables/base.py", line 5753, in invoke
    return self.bound.invoke(
           ~~~~~~~~~~~~~~~~~^
        input,
        ^^^^^^
        self._merge_configs(config),
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        **{**self.kwargs, **kwargs},
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "gemini3_openai_issue/.venv-buggy/lib/python3.13/site-packages/langchain_core/language_models/chat_models.py", line 472, in invoke
    self.generate_prompt(
    ~~~~~~~~~~~~~~~~~~~~^
        [self._convert_input(input)],
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    ...<6 lines>...
        **kwargs,
        ^^^^^^^^^
    ).generations[0][0],
    ^
  File "gemini3_openai_issue/.venv-buggy/lib/python3.13/site-packages/langchain_core/language_models/chat_models.py", line 1752, in generate_prompt
    return self.generate(prompt_messages, stop=stop, callbacks=callbacks, **kwargs)
           ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "gemini3_openai_issue/.venv-buggy/lib/python3.13/site-packages/langchain_core/language_models/chat_models.py", line 1559, in generate
    self._generate_with_cache(
    ~~~~~~~~~~~~~~~~~~~~~~~~~^
        m,
        ^^
    ...<2 lines>...
        **kwargs,
        ^^^^^^^^^
    )
    ^
  File "gemini3_openai_issue/.venv-buggy/lib/python3.13/site-packages/langchain_core/language_models/chat_models.py", line 1899, in _generate_with_cache
    result = self._generate(
        messages, stop=stop, run_manager=run_manager, **kwargs
    )
  File "gemini3_openai_issue/.venv-buggy/lib/python3.13/site-packages/langchain_openai/chat_models/base.py", line 1653, in _generate
    _handle_openai_bad_request(e)
    ~~~~~~~~~~~~~~~~~~~~~~~~~~^^^
  File "gemini3_openai_issue/.venv-buggy/lib/python3.13/site-packages/langchain_openai/chat_models/base.py", line 1650, in _generate
    raw_response = self.client.with_raw_response.create(**payload)
  File "gemini3_openai_issue/.venv-buggy/lib/python3.13/site-packages/openai/_legacy_response.py", line 367, in wrapped
    return cast(LegacyAPIResponse[R], func(*args, **kwargs))
                                      ~~~~^^^^^^^^^^^^^^^^^
  File "gemini3_openai_issue/.venv-buggy/lib/python3.13/site-packages/openai/_utils/_utils.py", line 298, in wrapper
    return func(*args, **kwargs)
  File "gemini3_openai_issue/.venv-buggy/lib/python3.13/site-packages/openai/resources/chat/completions/completions.py", line 1215, in create
    return self._post(
           ~~~~~~~~~~^
        "/chat/completions",
        ^^^^^^^^^^^^^^^^^^^^
    ...<51 lines>...
        stream_cls=Stream[ChatCompletionChunk],
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "gemini3_openai_issue/.venv-buggy/lib/python3.13/site-packages/openai/_base_client.py", line 1332, in post
    return cast(ResponseT, self.request(cast_to, opts, stream=stream, stream_cls=stream_cls))
                           ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "gemini3_openai_issue/.venv-buggy/lib/python3.13/site-packages/openai/_base_client.py", line 1105, in request
    raise self._make_status_error_from_response(err.response) from None
openai.BadRequestError: Error code: 400 - [{'error': {'code': 400, 'message': 'Function call is missing a thought_signature in functionCall parts. This is required for tools to work correctly, and missing thought_signature may lead to degraded model performance. Additional data, function call `default_api:greet` , position 2. Please refer to https://ai.google.dev/gemini-api/docs/thought-signatures for more details.', 'status': 'INVALID_ARGUMENT'}}]

Description

ChatOpenAI fails to provide thought signatures on multi-turn conversations when used with Gemini models, resulting in 400 BadRequestError.

When calling Gemini models, Google returns a thought signature (https://ai.google.dev/gemini-api/docs/thought-signatures). Thought signatures are encrypted representations of the model's internal thought process and are used to preserve reasoning context across multi-step interactions. According to Google's documentation:

As a general rule, if you receive a thought signature in a model response, you should pass it back exactly as received when sending the conversation history in the next turn. When using Gemini 3 models, you must pass back thought signatures during function calling, otherwise you will get a validation error (4xx status code).

When calling Gemini via ChatOpenAI, the thought signature is not passed in followup turns in a multi-turn conversation, thereby triggering a 400 BadRequestError.

System Info

System Information

OS: Darwin OS Version: Darwin Kernel Version 25.4.0: Thu Mar 19 19:33:25 PDT 2026; root:xnu-12377.101.15~1/RELEASE_ARM64_T6041 Python Version: 3.13.12 (main, Feb 12 2026, 01:06:02) [Clang 21.1.4 ]

Package Information

langchain_core: 1.3.3 langsmith: 0.8.3 langchain_openai: 1.2.1 langchain_protocol: 0.0.15

Optional packages not installed

deepagents deepagents-cli

Other Dependencies

httpx: 0.28.1 jsonpatch: 1.33 openai: 2.36.0 orjson: 3.11.9 packaging: 26.2 pydantic: 2.13.4 pyyaml: 6.0.3 requests: 2.33.1 requests-toolbelt: 1.0.0 tenacity: 9.1.4 tiktoken: 0.12.0 typing-extensions: 4.15.0 uuid-utils: 0.14.1 xxhash: 3.7.0 zstandard: 0.25.0

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

langchain - 💡(How to fix) Fix ChatOpenAI fails to provide thought signatures on multi-turn conversations when used with Gemini models, resulting in 400 BadRequestError