Repository navigation
google-vertex: Codex tool continuation returns 400 when functionCall thought_signature is not replayed #1254
Description
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-flashActual 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-flashUpstream 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-flashprovider succeeds through/v1/responsesfor 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
functionCallpart is missing its requiredthought_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
Ingwannu commentedon Aug 8, 2026
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, andparseResponsewire it only whengoogleMode === "cloud-code-assist". The Vertex branch compiles and sends the same GeminifunctionCallparts 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:
thoughtSignaturebeside the matchingfunctionCall;devPR.I will post the PR and verification here; the issue stays open until the fix lands on
dev.Ingwannu commentedon Aug 8, 2026
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
functionCallon 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.brunoflma commentedon Aug 8, 2026
Same root cause reaches
google-antigravity(googleMode: cloud-code-assist), not onlygoogle-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()insrc/adapters/google.ts, which emits the signature under the camelCase key only:Emitting both spellings on the same part fixed the continuation failures for me:
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:
google-antigravityoverdaily-cloudcode-pa.googleapis.com, not Vertex overaiplatform.googleapis.com. I am inferring a shared cause from the shared code path and the identical symptom shape, not from a Vertex capture.thought_signaturespecifically 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.
github-actions commentedon Aug 8, 2026
Automated translation bookkeeping — detected language: unknown.
Wibias commentedon Aug 8, 2026
I will be closing this as fixed by #1266. @brunoflma Please open a new issue for that
brunoflma commentedon Aug 10, 2026
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_signatureI suggested is not needed.google-antigravity-replay.tsinjects only the camelCasethoughtSignatureand 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.