#Gemini models fail on tool calls

1 messages · Page 1 of 1 (latest)

bitter oak
#

Bug Report: Google Generative AI API Error (400) - Missing thought_signature on Tool Calls with Gemini models

Description:
When using Gemini models through AI Studio API keys (like google/gemini-flash-latest) with thinking mode enabled (even on low), subsequent tool executions fail with a HTTP 400 error. The API rejects the turn transition because the function call payload returned to the model or processed by the gateway is missing the required thought_signature.

Error Message:

Google Generative AI API error (400): 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 `...` [code=INVALID_ARGUMENT]

Steps to Reproduce:

  1. Configure session to use a Google/Gemini model with thinking enabled (e.g., thinking=low or medium).
  2. Trigger any tool call.
  3. The tool executes successfully on the host/node, but when the gateway passes the toolResult back to the Google API to generate the final response, the API returns a 400 error complaining about the missing thought_signature in the functionCall parts.

Expected Behavior:
The gateway should correctly preserve, validate, or bridge the thought_signature returned by Gemini during the initial assistant turn containing the functionCall, and include it when submitting the tool response back to the Google API, or automatically disable/fallback gracefully if the API strict mode is violated.

Version:
Current dev, main branch (2026.5.19, a134683)

hollow citrusBOT
spark helmBOT
# bitter oak **Bug Report: Google Generative AI API Error (400) - Missing `thought_signature`...

I checked current origin/main at a134683. This looks like a real bug report, not a config issue.

Relevant source I found: src/agents/openai-transport-stream.ts already captures Gemini extra_content.google.thought_signature from streamed tool calls and re-emits it on replay. It also has a fallback skip_thought_signature_validator, but that fallback is currently gated by model.id.toLowerCase().includes("gemini-3").

That makes your repro interesting: google/gemini-flash-latest is one of the alias-style refs that docs say gets Gemini thinking handling, but the fallback gate may not classify it as requiring the thought-signature workaround. So if Google is enforcing thought_signature for that route, the current guard can miss it.

Practical workaround to try: use the explicit Gemini model ref from the Google provider docs instead of *-latest, or turn thinking off for that model when tools are needed. Docs: https://docs.openclaw.ai/providers/google

This is worth filing as a GitHub issue with the exact resolved model/status output and the failing gateway log line: https://github.com/openclaw/openclaw/issues

Useful details to include:

  • openclaw --version (you already have 2026.5.19 / a134683)
  • API key route vs Gemini CLI/OAuth route
  • exact configured model ref and the resolved model shown by openclaw status --all
  • whether the same tool call works with an explicit Gemini ref instead of google/gemini-flash-latest
bitter oak
#

This is not going to work as I want to use gemini 3.5 flash, which is missing from the list of known models

spark helmBOT
# bitter oak This is not going to work as I want to use gemini 3.5 flash, which is missing fr...

Right, *-latest is the awkward part here.

You should be able to add the exact AI Studio model ID manually instead of waiting for the built-in catalog. The docs say custom/provider models need both:

  • models.providers.<provider>.models[] to register the runtime model
  • agents.defaults.models if you use an allowlist/model picker visibility

Example, replacing the id with the exact Google model id you see in AI Studio:

openclaw config set models.providers.google.models '[{"id":"gemini-3.5-flash","name":"Gemini 3.5 Flash","input":["text","image"],"reasoning":true}]' --strict-json --merge

openclaw config set agents.defaults.models '{"google/gemini-3.5-flash":{}}' --strict-json --merge

openclaw models set google/gemini-3.5-flash
spark helmBOT
# bitter oak This is not going to work as I want to use gemini 3.5 flash, which is missing fr...

Docs for the two relevant pieces:
https://docs.openclaw.ai/concepts/model-providers
https://docs.openclaw.ai/concepts/models

One useful detail: the current code’s thought-signature fallback is gated on model ids containing gemini-3. So an explicit id like gemini-3.5-flash is more likely to hit the workaround than gemini-flash-latest.

If that still fails with the same thought_signature 400, then the issue is probably not just the missing catalog row; it means the Google OpenAI-compatible replay path needs to classify this new model family more broadly. That should go in the GitHub issue with the exact configured id and openclaw status --all output.

bitter oak
#

with your config, it tries to contact openai for gemini models and fails on auth. lol. I guess it's using the codex harness?

19:00:49+00:00 warn model-fallback/decision {"subsystem":"model-fallback/decision"} {"event":"model_fallback_decision","tags":["error_handling","model_fallback","candidate_succeeded"],"runId":"c9a7dc37-7690-4f52-9554-00c9df9dc2e7","sessionId":"c9a7dc37-7690-4f52-9554-00c9df9dc2e7","lane":"cron-nested","decision":"candidate_succeeded","requestedProvider":"google","requestedModel":"gemini-3.5-flash","candidateProvider":"opencode-go","candidateModel":"glm-5.1","attempt":2,"total":13,"fallbackStepType":"fallback_step","fallbackStepFromModel":"google/gemini-3.5-flash","fallbackStepToModel":"opencode-go/glm-5.1","fallbackStepFromFailureReason":"auth","fallbackStepFromFailureDetail":"401 Incorrect API key provided: AIzaSyHKcB0Y. You can find your API key at https://platform.openai.com/account/api-keys.","fallbackStepChainPosition":2,"fallbackStepFinalOutcome":"succeeded","isPrimary":false,"requestedModelMatched":false,"fallbackConfigured":true,"previousAttempts":[{"provider":"google","model":"gemini-3.5-flash","reason":"auth","status":401,"errorPreview":"401 Incorrect API key provided: AIzaSyHKcB0Y. You can find your API key at https://platform.openai.com/account/api-keys.","errorHash":"sha256:b7e548ca9bef"}]} model fallback decision

spark helmBOT
# bitter oak with your config, it tries to contact openai for gemini models and fails on auth...

Yep, that one is on me. The minimal models.providers.google.models entry can make google behave like an explicit OpenAI-compatible provider without the Google base URL, so the SDK defaults to api.openai.com and sends the Gemini key there. That is exactly what your log shows.

Patch the provider fields, keeping your custom model row:

openclaw config set models.providers.google.api '"openai-completions"' --strict-json --merge
openclaw config set models.providers.google.baseUrl '"https://generativelanguage.googleapis.com/v1beta/openai"' --strict-json --merge

If your key is not already coming from GEMINI_API_KEY / GOOGLE_API_KEY, also set the Google key source, not an OpenAI key:

openclaw config set models.providers.google.apiKey '"${GEMINI_API_KEY}"' --strict-json --merge

Then restart the gateway and check:

spark helmBOT
# bitter oak with your config, it tries to contact openai for gemini models and fails on auth...
openclaw status --all
openclaw models list --provider google

Docs source for why this happened: custom/provider models live under models.providers, and a custom provider with missing baseUrl + api: "openai-completions" falls back to OpenAI-compatible defaults. https://docs.openclaw.ai/concepts/model-providers

So no, probably not the Codex harness. It is the OpenAI-compatible transport defaulting to OpenAI because my config snippet was incomplete. Sorry, claw slipped there.

bitter oak
#

Well.

19:15:14+00:00 warn agent/embedded {"subsystem":"agent/embedded"} {"event":"embedded_run_agent_end","tags":["error_handling","lifecycle","agent_end","assistant_error"],"runId":"e5fd406f-a21c-4332-bc12-0b737b044d83","isError":true,"error":"503 status code (no body)","failoverReason":"timeout","model":"gemini-3.5-flash","provider":"google","rawErrorPreview":"503 status code (no body)","rawErrorHash":"sha256:68a01de63496","providerRuntimeFailureKind":"timeout"} embedded run agent end

spark helmBOT
# bitter oak Well. 19:15:14+00:00 warn agent/embedded {"subsystem":"agent/embedded"} {"even...

That at least means the request is now reaching Google, not OpenAI. The new failure is different: 503 status code (no body) from provider google, model gemini-3.5-flash.

Next thing I’d verify is whether that exact model id is actually available to your AI Studio key:

curl -sS -H "x-goog-api-key: $GEMINI_API_KEY"   https://generativelanguage.googleapis.com/v1beta/models   | jq -r '.models[].name'   | grep -i 'gemini.*flash'

If models/gemini-3.5-flash is not in that list, the custom catalog row only makes OpenClaw try it; it can’t make Google serve it for that key/project.

If it is listed, try a tiny no-tool request against the native Gemini endpoint to separate “model availability” from “OpenClaw tool-call replay”:

curl -sS -H "x-goog-api-key: $GEMINI_API_KEY"   -H "Content-Type: application/json"   https://generativelanguage.googleapis.com/v1beta/models/gemini-3.5-flash:generateContent   -d '{"contents":[{"parts":[{"text":"Reply OK"}]}]}'

My read:

spark helmBOT
bitter oak
#

turns out to have been an issue on Google'a side, works fine now. solved, I guess, though the -latest model behavior is definitely a bug

spark helmBOT
# bitter oak turns out to have been an issue on Google'a side, works fine now. solved, I gues...

Good, that lines up with the 503 being provider-side.

I agree on the remaining bug: google/gemini-flash-latest should not bypass the Gemini thought-signature handling just because the model id does not contain gemini-3. The explicit gemini-3.5-flash workaround is fine for now, but the *-latest alias behavior is worth tracking.

If this thread is effectively solved, please right-click the solution message → Apps → ✅ Mark Solution so Answer Overflow indexes the useful bits for the next person.