Skip to content

google (AI Studio): Codex tool continuation returns 400 — thought_signature replay is gated to vertex/cloud-code-assist only #1930

Description

@YuYingRay

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

  • I searched existing provider and compatibility issues.
  • The request and response were redacted.
  • The expected behaviour is based on an upstream specification or a concrete client requirement.

Metadata

Metadata

Assignees

No one assigned

    Labels

    providerProvider adapters, OpenAI-compat presets, upstream API quirksprovider-compatibilityProvider compatibility reportstoolstool_calls, MCP, web-search / sidecar tools

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions