Skip to content

google-vertex: Codex tool continuation returns 400 when functionCall thought_signature is not replayed #1254

Closed

Description

@akazatmd-ctrl

Client or integration

Codex CLI

Provider or upstream service

google-vertex (Vertex AI, service-account JSON / ADC)

OpenCodex version

2.10.2

Endpoint or capability

Codex CLI tool-call continuation through google-vertex/gemini-3.6-flash (/v1/responses translation / tool-call replay)

Current behaviour

Vertex connectivity and authentication are healthy, and tool-free requests succeed, but a Codex turn that invokes a tool fails on the continuation with HTTP 400.

A direct OpenCodex Responses request succeeds:

{
  "model": "google-vertex/gemini-3.6-flash",
  "input": "Reply exactly: VERTEX_OK",
  "stream": false
}

Response:

status: completed
model: gemini-3.6-flash
text: VERTEX_OK

A simple question sent from Codex also succeeds. The failure appears only after a function call, on the subsequent continuation turn.

Expected behaviour

The thought_signature / thoughtSignature returned by Gemini/Vertex for a function call should be preserved and replayed unchanged on the subsequent tool-result continuation, so Codex tool loops can continue successfully.

Minimal redacted request or reproduction

1. Configure a `google-vertex` provider using `adapter: google`, `googleMode: vertex`, and a service-account JSON exposed through ADC / `GOOGLE_APPLICATION_CREDENTIALS`.
2. Select `google-vertex/gemini-3.6-flash` in Codex CLI.
3. Send a tool-free prompt. It succeeds.
4. Send a prompt that requires a shell command or another Codex tool.
5. The function call is emitted, but the continuation fails with HTTP 400 because the prior `functionCall` part is missing its thought signature.

Redacted provider shape:
adapter:      google
googleMode:   vertex
auth:         service-account JSON via ADC
baseUrl:      https://aiplatform.googleapis.com
model:        gemini-3.6-flash

Actual response or error

Provider error 400: Vertex AI invalid request: 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:shell_command`, position 4.

OpenCodex records:
status: 400
error: invalid_request_error
route: explicit-provider
google-vertex/gemini-3.6-flash

Upstream documentation

https://ai.google.dev/gemini-api/docs/thought-signatures

Gemini requires thought signatures associated with function calls to be passed back unchanged in subsequent turns when using tools.

Suggested mapping or implementation notes

This looks like a replay / translation issue rather than a credential or connectivity problem:

  • OpenCodex proxy health is OK.
  • The same google-vertex/gemini-3.6-flash provider succeeds through /v1/responses for a tool-free request.
  • Codex tool-free requests succeed through the same provider.
  • Failure appears only after a function call, on the continuation turn.
  • Vertex explicitly reports that the prior functionCall part is missing its required thought_signature.

This looks related in concept to #897, where Antigravity replay dropped a reasoning signature carried separately from the function call. #897 was fixed for the google-antigravity replay path by #941. The google-vertex path may need the same class of signature preservation/replay, or it may currently bypass that replay cache.

Additional context and attachments

This is a resubmission of #1248 using the repository's Provider or API compatibility issue template after maintainer guidance to use the issue template.

Environment used for the reproduction:

  • Codex: codex-cli 0.145.0
  • OS: Windows 11 x64 (10.0.26200)
  • Model: google-vertex/gemini-3.6-flash

The Vertex project ID, service-account identity, credential path, request IDs, and conversation IDs are intentionally omitted.

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.

Activity

  1. Ingwannu commented on Aug 8, 2026

    @Ingwannu
    Owner

    Confirmed against current dev: this is a real transport-boundary bug, not an ADC/connectivity failure.

    The Google adapter already has a bounded, TTL/LRU thought-signature replay mechanism, but buildRequest, parseStream, and parseResponse wire it only when googleMode === "cloud-code-assist". The Vertex branch compiles and sends the same Gemini functionCall parts without observing the returned signature or re-injecting it on the next tool-result turn, which matches the reported 400 exactly.

    I am implementing the narrow fix now:

    1. reuse the existing bounded replay store for Vertex while giving Vertex a transport-scoped cache key so signatures cannot cross between Vertex and Antigravity;
    2. apply replay only after Google tool names have been compiled, preserving the existing function-call identity matching;
    3. observe signatures in both streaming and non-streaming Vertex responses;
    4. add a regression that performs a first Vertex function-call response and then proves the follow-up compiled request carries the exact opaque thoughtSignature beside the matching functionCall;
    5. run the focused Google/replay suites, typecheck, full tests, and privacy scan before opening a dev PR.

    I will post the PR and verification here; the issue stays open until the fix lands on dev.

  2. Ingwannu commented on Aug 8, 2026

    @Ingwannu
    Owner

    Fix is now available for maintainer review in #1266 (ae28b69e).

    The patch connects Vertex to the existing bounded thought-signature replay store, scopes the cache by Vertex transport/project/location plus the hashed session identity, and covers both streaming and non-streaming function-call responses. The regression proves the exact opaque signature is present beside the matching compiled functionCall on the subsequent tool-result request, and separately proves a Vertex signature cannot enter the Antigravity namespace.

    Verification completed locally: 94 focused Google/replay tests, typecheck, privacy scan, docs build (221 pages), and diff check. I requested reviews from @lidge-jun and @Wibias because this carries opaque provider-private replay state. I will not self-approve or self-merge; this issue remains open until the PR lands on dev.

  3. brunoflma commented on Aug 8, 2026

    @brunoflma

    Same root cause reaches google-antigravity (googleMode: cloud-code-assist), not only google-vertex — and the fix that worked for me is a one-line addition in the shared path, so it may be worth widening the scope of #1254 rather than fixing it per-mode.

    Both modes build their parts through messagesToGeminiFormat() in src/adapters/google.ts, which emits the signature under the camelCase key only:

    if (isLikelyRealThoughtSignature(tc.thoughtSignature)) part.thoughtSignature = tc.thoughtSignature;

    Emitting both spellings on the same part fixed the continuation failures for me:

    if (isLikelyRealThoughtSignature(tc.thoughtSignature)) {
      part.thoughtSignature = tc.thoughtSignature;
      part.thought_signature = tc.thoughtSignature;
    }

    I have been running that locally since 2.10.x and it is still needed on 2.11.0 — the released tag emits camelCase only.

    Two caveats on my evidence, so it is weighed correctly:

    • My route is google-antigravity over daily-cloudcode-pa.googleapis.com, not Vertex over aiplatform.googleapis.com. I am inferring a shared cause from the shared code path and the identical symptom shape, not from a Vertex capture.
    • I did not isolate whether the endpoint requires thought_signature specifically or merely accepts it. Sending both keys made the failure go away, which does not by itself prove the camelCase key is rejected — someone with a Vertex capture could settle that, and if the snake_case key turns out to be the only accepted one, emitting both is redundant rather than wrong.

    Happy to run anything specific against the antigravity route if it helps narrow it.

  4. github-actions commented on Aug 8, 2026

    @github-actions
    Contributor

    Automated translation bookkeeping — detected language: unknown.

  5. Wibias commented on Aug 8, 2026

    @Wibias
    Contributor

    I will be closing this as fixed by #1266. @brunoflma Please open a new issue for that

  6. brunoflma commented on Aug 10, 2026

    @brunoflma

    Opened as #1429 per your request, @Wibias — thanks.

    Two corrections to what I wrote here earlier, both from measurements I ran on 2.12.0 rather than from reading:

    The snake_case thought_signature I suggested is not needed. google-antigravity-replay.ts injects only the camelCase thoughtSignature and those requests are accepted, so the endpoint takes camelCase fine. I had been carrying a local patch emitting both; I dropped it.

    And my original evidence here was weaker than I presented it. Sending both keys made my failures go away, so I attributed the fix to the snake_case key — but the real variable was elsewhere: my A/B passed in both arms because the in-process replay cache was supplying the signature either way, masking the thing I thought I was measuring. What separates the cases is restarting the proxy between turns to empty that cache. That is the repro in #1429, and it is the actual antigravity gap.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions