Client or integration
Codex CLI
Provider or upstream service
google (Gemini AI Studio API, adapter: google)
OpenCodex version
2.20.0
Endpoint or capability
/v1/responses — tool calls, multi-turn continuation
Current behaviour
Any tool-using turn against the plain google provider fails on the continuation request with HTTP 400 from Gemini:
Function call is missing a thought_signature in functionCall parts.
The first turn succeeds and the tool executes. The follow-up request that replays the tool call is rejected. Plain chat turns (no tools) always work.
Root cause: every thought-signature replay call site in src/adapters/google.ts (2.20.0) is gated on provider.googleMode:
| Line |
Call |
Guard |
| 488 |
applyAntigravityReplay |
inside googleMode === "cloud-code-assist" branch (opens L442) |
| 520 |
applyAntigravityReplay |
inside googleMode === "vertex" branch (opens L509) |
| 673 |
observeAntigravityReplay |
googleMode === "cloud-code-assist" || === "vertex" (L671) |
| 900 |
observeAntigravityReplay |
googleMode === "cloud-code-assist" || === "vertex" (L898) |
The google registry entry (src/providers/registry.ts, ~L1444) declares no googleMode:
{ id: "google", label: "Google Gemini", adapter: "google",
baseUrl: "https://generativelanguage.googleapis.com", authKind: "key", ... }
so provider.googleMode is undefined and none of the four branches run. Both halves are skipped: signatures are never observed from responses and never applied to continuations. Because the write half is also skipped, the failure cannot self-heal on retry.
antigravityUsesReplayCache() is not the gate — it only excludes Claude models and would accept a Gemini model; it is simply never reached on the AI Studio path.
Inbound-wire comparison (same provider, same model, same replay payload — only the inbound protocol differs):
| Inbound wire |
Client |
Result |
Responses (/v1/responses) |
Codex CLI / App |
400 |
Chat Completions (/v1/chat/completions) |
OpenAI-compatible clients |
200 |
Anthropic Messages (/v1/messages) |
Claude Code |
200 |
The Chat and Anthropic paths rebuild functionCall parts rather than carrying an upstream part that Gemini expects a signature on, so they are unaffected. This makes google unusable in Codex for anything agentic, while remaining fine for chat.
Affected models tested: gemini-3.5-flash, gemini-3.5-flash-lite.
Expected behaviour
The thought_signature returned on a functionCall part should be recorded and replayed verbatim on the continuation request, so a multi-turn tool loop against the google provider succeeds — matching the behaviour already implemented for google-vertex and google-antigravity.
Per Google's specification this is mandatory, not optional: Gemini 3 models enforce strict validation, signatures must be returned in subsequent requests even at MINIMAL thinking levels, and the part must be included exactly as returned by the model. Omitting it is documented to produce exactly the 400 seen here.
Minimal redacted request or reproduction
# Against a running proxy with a `google` provider configured (AI Studio API key).
# No Codex needed — a plain Responses request with a tool replay reproduces it.
curl -s http://127.0.0.1:10100/v1/responses \
-H 'content-type: application/json' \
-d '{
"model": "google/gemini-3.5-flash-lite",
"tools": [{"type":"function","name":"get_x","description":"d",
"parameters":{"type":"object","properties":{"a":{"type":"string"}},"required":["a"]}}],
"input": [
{"type":"message","role":"user","content":[{"type":"input_text","text":"list files"}]},
{"type":"function_call","call_id":"k1","name":"get_x","arguments":"{\"a\":\"1\"}"},
{"type":"function_call_output","call_id":"k1","output":"2"}
]
}'
# type:"custom" tools (custom_tool_call / custom_tool_call_output, what Codex 0.144
# actually emits for its exec tool) fail identically.
# Via Codex:
codex exec --skip-git-repo-check -m "google/gemini-3.5-flash-lite" \
-c 'sandbox_mode="read-only"' \
"Use the shell tool to list files in the current directory, then tell me how many there are."
# The shell command runs and returns successfully, then the continuation 400s.
# NOTE: if you instead get "User location is not supported for the API use."
# (FAILED_PRECONDITION), that is an unrelated Google egress-IP restriction, not this
# bug. This bug is identifiable by the "thought_signature" wording.
Actual response or error
# HTTP 400 from the proxy:
{"error":{"message":"Provider error 400: {\n \"error\": {\n \"code\": 400,\n \"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, ...\",\n \"status\": \"INVALID_ARGUMENT\"\n }\n}\n","type":"upstream_error","code":null}}
# As surfaced in Codex, after the tool has already executed successfully:
exec ... succeeded in 1658ms: 2
ERROR: {"error":{"message":"Provider error 400","type":"upstream_error","code":null}}
# Same request via /v1/chat/completions on the same provider and model: HTTP 200.
Upstream documentation
Upstream documentation
Google AI for Developers — Thought Signatures:
https://ai.google.dev/gemini-api/docs/thought-signatures
Function calling with the Gemini API:
https://ai.google.dev/gemini-api/docs/function-calling
Key requirements stated there:
- Gemini 3 models enforce stricter validation on thought signatures than earlier versions.
- Signatures from previous responses must be returned in subsequent requests, even when using MINIMAL thinking levels.
- The part containing the
functionCall and its thought_signature must be included exactly as returned by the model.
- A missing required signature produces a 400 error.
- For parallel function calls, the signature is attached only to the first
functionCall part in a response.
Suggested mapping or implementation notes
Include the AI Studio path in the same guards — treat an absent googleMode (or an explicit "ai-studio") as replay-eligible at all four sites, so signatures are both observed from responses and applied to continuations.
The cache in google-antigravity-replay.ts looks provider-agnostic (keyed by model + session + functionCall identity), so this may be a wiring change rather than new machinery. Worth confirming the session key is meaningful on the AI Studio path, since it has no Antigravity/Vertex session concept.
If AI Studio genuinely cannot round-trip signatures, an explicit upfront error naming the limitation would be far better than an opaque upstream 400 after the tool has already run and had side effects.
Additional context and attachments
This is not a duplicate of the existing thought-signature issues. Those all concern providers where the replay machinery is already wired in, and address cache misses within it:
On the plain google provider the replay path is never entered at all, for any request — a coverage gap rather than a cache-correctness bug. That is why the vertex/CCA fixes do not resolve it.
Environment: opencodex 2.20.0 (npm, bundled Bun) · Windows 10 Pro 19045 · Node v24.11.1 · Codex CLI 0.144.1 · provider google, adapter google, authKind: key.
Unrelated minor finding: two models listed in the google registry entry return HTTP 404 from the AI Studio API (they appear in the Codex model picker but cannot be called):
gemini-3.6-flash → 404
gemini-3.7-flash → 404
(gemini-3.1-pro-preview returned 429 in my testing, likely quota rather than availability.) Happy to split this into its own issue if preferred.
Checks
Client or integration
Codex CLI
Provider or upstream service
google (Gemini AI Studio API, adapter: google)
OpenCodex version
2.20.0
Endpoint or capability
/v1/responses — tool calls, multi-turn continuation
Current behaviour
Any tool-using turn against the plain
googleprovider fails on the continuation request with HTTP 400 from Gemini:The first turn succeeds and the tool executes. The follow-up request that replays the tool call is rejected. Plain chat turns (no tools) always work.
Root cause: every thought-signature replay call site in
src/adapters/google.ts(2.20.0) is gated onprovider.googleMode:applyAntigravityReplaygoogleMode === "cloud-code-assist"branch (opens L442)applyAntigravityReplaygoogleMode === "vertex"branch (opens L509)observeAntigravityReplaygoogleMode === "cloud-code-assist" || === "vertex"(L671)observeAntigravityReplaygoogleMode === "cloud-code-assist" || === "vertex"(L898)The
googleregistry entry (src/providers/registry.ts, ~L1444) declares nogoogleMode:so
provider.googleModeisundefinedand none of the four branches run. Both halves are skipped: signatures are never observed from responses and never applied to continuations. Because the write half is also skipped, the failure cannot self-heal on retry.antigravityUsesReplayCache()is not the gate — it only excludes Claude models and would accept a Gemini model; it is simply never reached on the AI Studio path.Inbound-wire comparison (same provider, same model, same replay payload — only the inbound protocol differs):
/v1/responses)/v1/chat/completions)/v1/messages)The Chat and Anthropic paths rebuild
functionCallparts rather than carrying an upstream part that Gemini expects a signature on, so they are unaffected. This makesgoogleunusable in Codex for anything agentic, while remaining fine for chat.Affected models tested:
gemini-3.5-flash,gemini-3.5-flash-lite.Expected behaviour
The
thought_signaturereturned on afunctionCallpart should be recorded and replayed verbatim on the continuation request, so a multi-turn tool loop against thegoogleprovider succeeds — matching the behaviour already implemented forgoogle-vertexandgoogle-antigravity.Per Google's specification this is mandatory, not optional: Gemini 3 models enforce strict validation, signatures must be returned in subsequent requests even at MINIMAL thinking levels, and the part must be included exactly as returned by the model. Omitting it is documented to produce exactly the 400 seen here.
Minimal redacted request or reproduction
Actual response or error
Upstream documentation
Upstream documentation
Google AI for Developers — Thought Signatures:
https://ai.google.dev/gemini-api/docs/thought-signatures
Function calling with the Gemini API:
https://ai.google.dev/gemini-api/docs/function-calling
Key requirements stated there:
functionCalland itsthought_signaturemust be included exactly as returned by the model.functionCallpart in a response.Suggested mapping or implementation notes
Include the AI Studio path in the same guards — treat an absent
googleMode(or an explicit"ai-studio") as replay-eligible at all four sites, so signatures are both observed from responses and applied to continuations.The cache in
google-antigravity-replay.tslooks provider-agnostic (keyed by model + session + functionCall identity), so this may be a wiring change rather than new machinery. Worth confirming the session key is meaningful on the AI Studio path, since it has no Antigravity/Vertex session concept.If AI Studio genuinely cannot round-trip signatures, an explicit upfront error naming the limitation would be far better than an opaque upstream 400 after the tool has already run and had side effects.
Additional context and attachments
This is not a duplicate of the existing thought-signature issues. Those all concern providers where the replay machinery is already wired in, and address cache misses within it:
On the plain
googleprovider the replay path is never entered at all, for any request — a coverage gap rather than a cache-correctness bug. That is why the vertex/CCA fixes do not resolve it.Environment: opencodex 2.20.0 (npm, bundled Bun) · Windows 10 Pro 19045 · Node v24.11.1 · Codex CLI 0.144.1 · provider
google, adaptergoogle,authKind: key.Unrelated minor finding: two models listed in the
googleregistry entry return HTTP 404 from the AI Studio API (they appear in the Codex model picker but cannot be called):gemini-3.6-flash→ 404gemini-3.7-flash→ 404(
gemini-3.1-pro-previewreturned 429 in my testing, likely quota rather than availability.) Happy to split this into its own issue if preferred.Checks