Skip to content

createGoogleThinkingPayloadWrapper: strip store/service_tier/metadata only for google-generative-ai (collides with Codex subscription path) #61376

@jcat59

Description

@jcat59

Summary

In createGoogleThinkingPayloadWrapper (provider-stream), Google OpenAI-compat requests are dispatched with top-level store, service_tier, and metadata fields still attached. Google's OpenAI-compatible endpoint rejects these as unknown fields with HTTP 400. The wrapper already has a model.api === "google-generative-ai" guard for sanitizeGoogleThinkingPayload but does not strip the unsupported OpenAI-only top-level fields.

Downstream operators who patch this locally tend to strip those fields unconditionally in the wrapper, which then breaks the OpenAI Codex subscription path (openai-codex-responses api), because Codex requires store: false in its request body and returns HTTP 400 {"detail":"Store must be set to false"} when the field is missing.

The correct fix is to strip store / service_tier / metadata only when model.api === "google-generative-ai", co-located with the existing sanitizeGoogleThinkingPayload guard.

Environment

  • OpenClaw: 2026.3.28
  • Affected file (installed): /usr/lib/node_modules/openclaw/dist/provider-stream-*.js
  • Affected function: createGoogleThinkingPayloadWrapper
  • Affected provider api: google-generative-ai (Google OpenAI-compat endpoint: https://generativelanguage.googleapis.com/v1beta/openai/)
  • Colliding provider api: openai-codex-responses (Codex subscription path, https://chatgpt.com/backend-api)

Current Upstream Code (2026.3.28)

function createGoogleThinkingPayloadWrapper(baseStreamFn, thinkingLevel) {
  const underlying = baseStreamFn ?? streamSimple;
  return (model, context, options) => {
    return streamWithPayloadPatch(underlying, model, context, options, (payload) => {
      if (model.api === "google-generative-ai") sanitizeGoogleThinkingPayload({
        payload,
        modelId: model.id,
        thinkingLevel
      });
    });
  };
}

Buggy Behavior

When a model with api: "google-generative-ai" is routed through this wrapper, the payload still contains OpenAI-compat-only top-level fields:

  • store (boolean)
  • service_tier (string)
  • metadata (object)

Google's OpenAI-compat endpoint rejects requests containing any of these with HTTP 400 INVALID_ARGUMENT / unknown field. This is the same failure class reported in #53658 (fallback/replay path).

Expected Behavior

For model.api === "google-generative-ai", the wrapper should scrub the OpenAI-only top-level fields before dispatch, the same place it already runs sanitizeGoogleThinkingPayload.

Why This Matters Beyond Gemini — Codex Collision

Naive downstream fixes strip these fields unconditionally inside the wrapper. That regresses the Codex subscription path because OpenAI Codex's openai-codex-responses api requires store: false in every request body. When the field is stripped:

HTTP 400
{"detail":"Store must be set to false"}

This means the fix must be narrow — Google-api-only — or it will take out Codex agents on the same gateway.

Smallest Correct Fix

function createGoogleThinkingPayloadWrapper(baseStreamFn, thinkingLevel) {
  const underlying = baseStreamFn ?? streamSimple;
  return (model, context, options) => {
    return streamWithPayloadPatch(underlying, model, context, options, (payload) => {
      if (model.api === "google-generative-ai") {
        sanitizeGoogleThinkingPayload({
          payload,
          modelId: model.id,
          thinkingLevel
        });
        if (payload && typeof payload === "object") {
          delete payload.store;
          delete payload.service_tier;
          delete payload.metadata;
        }
      }
    });
  };
}

Key constraint: the delete block must be inside the model.api === "google-generative-ai" guard. Do not strip these fields for any other provider api.

Suggested Regression Tests

  • createGoogleThinkingPayloadWrapper with model.api === "google-generative-ai": asserts store/service_tier/metadata are stripped.
  • Same wrapper with model.api === "openai-codex-responses" and store: false on the payload: asserts store: false is preserved.
  • Same wrapper with model.api === "openai" and service_tier set: asserts it is preserved.

Related

Notes

Local operator workaround (retired on fix): a dist-level patch on provider-stream-*.js adds the Google-guarded delete block above. Tracked locally as OC016 with the removal trigger "upstream ships conditional guard natively."

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions