Skip to content

fix(google): omit request config with cached content#84958

Merged
steipete merged 2 commits into
openclaw:mainfrom
neeravmakwana:fix/gemini-cached-content-payload
May 25, 2026
Merged

fix(google): omit request config with cached content#84958
steipete merged 2 commits into
openclaw:mainfrom
neeravmakwana:fix/gemini-cached-content-payload

Conversation

@neeravmakwana

@neeravmakwana neeravmakwana commented May 21, 2026

Copy link
Copy Markdown
Contributor

Summary

Root Cause

OpenClaw had two ways to put cachedContent on direct Gemini GenerateContent requests:

  • explicit params.cachedContent, serialized by the Google transport itself
  • managed cacheRetention, where the embedded-runner Google prompt-cache wrapper creates a Gemini cachedContents resource and patches cachedContent into the transport payload

The explicit path sent cachedContent while still serializing request-level systemInstruction, tools, and toolConfig. The managed path stripped the request system prompt, but it still let request tools serialize before patching cachedContent into the payload. Google requires system instructions and tools/tool config to be set on the cached content resource when cachedContent is used; sending them again on GenerateContent can return HTTP 400.

Fix

  • Direct Google transport now treats non-empty options.cachedContent as a cached-content request and skips request-level systemInstruction, tools, and toolConfig.
  • Managed Gemini prompt caching now includes the request tool declarations and tool config in the Gemini cachedContents.create request when tools are present.
  • The managed cache key now includes a digest of the cached tool/toolConfig payload, so a cached content resource is not reused across different tool surfaces.
  • The managed wrapper now removes both systemPrompt and tools from the downstream GenerateContent context after it has created or reused the matching cached content.

Why This Is Safe

Uncached Gemini requests are unchanged: they still send system prompts and tools directly on GenerateContent. Cached Gemini requests still include contents and generation settings such as temperature, max tokens, and thinking config.

Security and runtime controls are unchanged for auth resolution, guarded model fetch, request URL/header construction, tool execution policy, and Slack/channel policy. This fix changes provider payload construction and managed cache contents only; it does not rely on prompt text to enforce the policy.

Real Behavior Proof

Behavior addressed: Gemini GenerateContent requests using managed cacheRetention: "long" with tools no longer send request-level systemInstruction, tools, or toolConfig together with cachedContent, avoiding Google 400 INVALID_ARGUMENT.

Real environment tested: Local macOS checkout, PR head 198a42bbc6, real Google Gemini API, google/gemini-2.5-flash, Gemini API key read from 1Password/Molty service account without logging the secret.

Exact steps or command run after this patch: Created a large managed prompt-cache request through prepareGooglePromptCacheStreamFn with extraParams: { cacheRetention: "long" }, one tool declaration, toolChoice: "auto", and createGoogleGenerativeAiTransportStreamFn; captured the downstream GenerateContent payload via onPayload; waited for stream.result(); deleted the created cachedContents/... resource. Also ran the same current-main managed path against google/gemini-3.5-flash to confirm the pre-fix 400.

Evidence after fix:

OPENCLAW_GEMINI_MANAGED_CACHE_TTKUCZ.payload:{"cachedContent":"present","systemInstruction":false,"tools":false,"toolConfig":false,"generationConfig":true}
OPENCLAW_GEMINI_MANAGED_CACHE_TTKUCZ.ok:true
OPENCLAW_GEMINI_MANAGED_CACHE_TTKUCZ.stop:stop
OPENCLAW_GEMINI_MANAGED_CACHE_TTKUCZ.contentTypes:text
OPENCLAW_GEMINI_MANAGED_CACHE_TTKUCZ.cacheRead:11886
OPENCLAW_GEMINI_MANAGED_CACHE_TTKUCZ.usageTotal:11957
managed.cleanupStatus:200
managed.entryStatus:ready
managed.cacheConfigDigestLen:252

Current-main repro before fix:

model:gemini-3.5-flash
managed.created:true
payload:{"cachedContent":"present","systemInstruction":false,"tools":true,"toolConfig":true,"generationConfig":true}
result.stop:error
result.cacheRead:0
result.errorMessage:Google Generative AI API error (400): CachedContent can not be used with GenerateContent request setting system_instruction, tools or tool_config. Proposed fix: move those values to CachedContent from GenerateContent request. [code=INVALID_ARGUMENT]
cache.cleanupStatus:200
cache.entryStatus:ready

Observed result after fix: The real managed cacheRetention: "long" request created and used Gemini cached content, omitted all three conflicting request-level keys, returned text with the marker, reported cacheRead=11886, and cleaned up the cached content with HTTP 200.

What was not tested: Slack two-bot delivery was not retested as the passing proof exercises the failing Google provider payload path directly; broad CI remains the merge gate for unrelated channels and platform coverage.

Tests

  • pnpm check:changed
  • pnpm test src/agents/pi-embedded-runner/google-prompt-cache.test.ts extensions/google/transport-stream.test.ts -- -t "google prompt cache|cachedContent|guarded fetch transport"
  • git diff --check
  • IDE lints for changed files: no diagnostics

Out Of Scope

  • Changing Gemini cache TTL or refresh timing.
  • Changing Slack/Socket Mode handling or two-bot QA harness behavior.
  • Adding compatibility fallbacks that send dynamic tools or system prompts alongside cachedContent.

Transparency

AI-assisted change; locally reviewed and validated with focused tests plus live managed Gemini provider proof.

Co-authored-by: Cursor <cursoragent@cursor.com>
@clawsweeper

clawsweeper Bot commented May 21, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge.

Workflow note: Future ClawSweeper reviews update this same comment in place.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

Summary
The branch changes Google Gemini cached-content payload construction so GenerateContent omits request-level system/tool config when cachedContent is present, and managed prompt caching stores tools/toolConfig on the cachedContents resource with regression tests.

Reproducibility: yes. at source level: current main can build a Gemini GenerateContent payload that combines cachedContent with request-level systemInstruction/tools, and the managed wrapper can patch cachedContent after leaving tools in the downstream context. I did not run a live failing Google request in this read-only review.

PR rating
Overall: 🐚 platinum hermit
Proof: 🦞 diamond lobster
Patch quality: 🐚 platinum hermit
Summary: Strong live provider logs and focused tests make this a good normal PR, with remaining confidence dependent on standard CI because this review was read-only.

Rank-up moves:

  • none
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

Real behavior proof
Sufficient (logs): The PR body includes redacted after-fix live Gemini logs showing corrected managed and explicit cached-content payloads and successful cached responses.

Risk before merge

  • I did not execute the contributor's claimed test commands in this read-only review, so required CI and maintainer validation should remain the merge gate.

Maintainer options:

  1. Decide the mitigation before merge
    Land this focused Google provider payload fix after normal checks, then close Bug: Gemini Context Caching Conflicts with systemInstruction and tools (PR #71441 closed without merge) #84919 as implemented.
  2. Pause or close
    Do not merge this PR until maintainers decide whether the risk is worth taking.

Next step before merge
No automated repair is needed; maintainers should handle ordinary review, required checks, and merge gating for the current PR head.

Security
Cleared: No concrete security or supply-chain concern was found; the diff is limited to Google provider payload construction and tests without changing auth resolution, dependencies, CI, or install scripts.

Review details

Best possible solution:

Land this focused Google provider payload fix after normal checks, then close #84919 as implemented.

Do we have a high-confidence way to reproduce the issue?

Yes at source level: current main can build a Gemini GenerateContent payload that combines cachedContent with request-level systemInstruction/tools, and the managed wrapper can patch cachedContent after leaving tools in the downstream context. I did not run a live failing Google request in this read-only review.

Is this the best way to solve the issue?

Yes. The patch fixes both explicit cachedContent and managed cacheRetention entry points, keeps uncached requests unchanged, places managed tool settings on cachedContents.create, and adds focused regression coverage.

Label justifications:

  • P2: This is a normal Google provider bug fix with limited blast radius and a linked reproduction path for cached Gemini requests.
  • rating: 🐚 platinum hermit: Current PR rating is 🐚 platinum hermit because proof is 🦞 diamond lobster, patch quality is 🐚 platinum hermit, and Strong live provider logs and focused tests make this a good normal PR, with remaining confidence dependent on standard CI because this review was read-only.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (logs): The PR body includes redacted after-fix live Gemini logs showing corrected managed and explicit cached-content payloads and successful cached responses.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR body includes redacted after-fix live Gemini logs showing corrected managed and explicit cached-content payloads and successful cached responses.

What I checked:

  • Current main direct cachedContent conflict: Current main trims and sends options.cachedContent, then still serializes systemInstruction, tools, and toolConfig when the context has a system prompt/tools, matching the linked issue's failing GenerateContent shape. (extensions/google/transport-stream.ts:713, 8961eae3f022)
  • Current main managed cache conflict: Current main creates managed cachedContents with only systemInstruction and then calls the downstream stream with tools still present, so the later payload patch can combine cachedContent with request tools. (src/agents/pi-embedded-runner/google-prompt-cache.ts:239, 8961eae3f022)
  • PR direct transport fix: The PR computes a trimmed cachedContent guard and skips request-level systemInstruction and tools/toolConfig when it is present, while preserving contents and generationConfig. (extensions/google/transport-stream.ts:706, 198a42bbc681)
  • PR managed cache fix: The PR serializes managed tool declarations/toolConfig into cachedContents.create, keys cache reuse by that config, and removes tools from the downstream cached GenerateContent context. (src/agents/pi-embedded-runner/google-prompt-cache.ts:189, 198a42bbc681)
  • Regression coverage: The PR updates tests for explicit cachedContent omission and managed cachedContents.create including systemInstruction, tools, and toolConfig while the downstream cached request omits systemPrompt/tools. (src/agents/pi-embedded-runner/google-prompt-cache.test.ts:179, 198a42bbc681)
  • Upstream API contract check: Google's current API docs expose tools[], toolConfig, and systemInstruction on CachedContent and show cachedContent as a GenerateContent config field; this supports moving cached tool/system config to cachedContents and not duplicating it on the request.

Likely related people:

  • vincentkoc: Public commit history shows b5f8cd4fcf58865888d6982b3e2021a69ff49420 added managed Gemini prompt caching, including the file this PR now extends. (role: feature introducer; confidence: high; commits: b5f8cd4fcf58, 2d6e75ccd57b, 1d736dcbbc93; files: src/agents/pi-embedded-runner/google-prompt-cache.ts, src/agents/pi-embedded-runner/google-prompt-cache.test.ts)
  • steipete: Public history shows 85826c83e4a10e312ff415dc8d3f58528e9fb611 moved Gemini transport into the Google plugin and later commits touched the same transport surface. (role: adjacent transport owner; confidence: medium; commits: 85826c83e4a1, 8dd91b14d3c0, cc0f3067a003; files: extensions/google/transport-stream.ts, extensions/google/transport-stream.test.ts)

Codex review notes: model gpt-5.5, reasoning high; reviewed against 8961eae3f022.

@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P2 Normal backlog priority with limited blast radius. merge-risk: 🚨 auth-provider 🚨 May break OAuth, tokens, provider routing, model choice, or credentials. labels May 21, 2026
@clawsweeper

clawsweeper Bot commented May 21, 2026

Copy link
Copy Markdown
Contributor

ClawSweeper PR egg

✨ Hatched: 🥚 common Clockwork Clawlet

Hatch command

Comment @clawsweeper hatch when this PR is hatchable.

Hatchability rules:

  • Merged PRs are hatchable.
  • Open PRs are hatchable when they are status: 👀 ready for maintainer look, status: 🚀 automerge armed, or labeled clawsweeper:automerge.
  • Closed unmerged PRs are hatchable only when one of those hatchable labels is still present in the durable record.

Rarity: 🥚 common.
Trait: sparkles near resolved comments.
Image traits: location CI tidepool; accessory rollback rope; palette seafoam, black, and opal; mood celebratory; pose waving from a small platform; shell paper lantern shell; lighting gentle morning glow; background little resolved-comment flags.
Share on X: post this hatch
Copy: My PR egg hatched a 🥚 common Clockwork Clawlet in ClawSweeper.

What is this egg doing here?
  • Eggs appear after the PR passes real-behavior proof. It is here for vibes, not verdicts: it does not change labels, ratings, merge decisions, or automation.
  • The shell reacts to review momentum: open follow-up work warms it up, re-review makes it wobble, and a clean final review lets it hatch.
  • Hatchability usually comes from sufficient real-behavior proof, no blocking P0/P1/P2 findings, no security attention needed, and clean correctness. A merged PR is already final, so merge makes the egg hatchable independently.
  • The hatch is seeded from this repository and PR number, so the same PR keeps the same creature; the reviewed head SHA can only change safe visual details.
  • Rarity is just collectible sparkle: 🥚 common, 🌱 uncommon, 💎 rare, ✨ glimmer, and 🌈 legendary.

@openclaw-barnacle openclaw-barnacle Bot added the agents Agent runtime and tooling label May 21, 2026
@neeravmakwana

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 🔁 re-review loop A fresh ClawSweeper review was explicitly requested after the latest review. and removed rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. merge-risk: 🚨 auth-provider 🚨 May break OAuth, tokens, provider routing, model choice, or credentials. labels May 21, 2026
@clawsweeper

clawsweeper Bot commented May 21, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

Re-review progress:

@clawsweeper clawsweeper Bot added status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. and removed status: 🔁 re-review loop A fresh ClawSweeper review was explicitly requested after the latest review. labels May 21, 2026
@giodl73-repo giodl73-repo removed the triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. label May 21, 2026
@openclaw-barnacle openclaw-barnacle Bot added the triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. label May 21, 2026
@steipete steipete self-assigned this May 25, 2026
@openclaw-barnacle openclaw-barnacle Bot added proof: supplied External PR includes structured after-fix real behavior proof. and removed triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. proof: sufficient ClawSweeper judged the real behavior proof convincing. labels May 25, 2026
@steipete steipete merged commit 148db14 into openclaw:main May 25, 2026
172 of 192 checks passed
@steipete

Copy link
Copy Markdown
Contributor

Landed in 148db14736013b0878345d2bfd80b1fe7057ac95.

  • Gate: required PR checks green; Real behavior proof run 26418980918 passed on head 198a42bbc68107a47ee38e577f2a5aaa5e8bcf56 after the proof body fields were normalized.
  • Live repro/fix proof: current main reproduced the managed cacheRetention: "long" + tools failure against google/gemini-3.5-flash with Google 400 INVALID_ARGUMENT; PR head succeeded against real Gemini with cachedContent present, request-level systemInstruction/tools/toolConfig absent, cacheRead=11886, and cached-content cleanup HTTP 200.
  • Local closeout: pulled main after merge; checkout is clean at 148db14736.

Thanks @neeravmakwana!

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

Labels

agents Agent runtime and tooling extensions: google P2 Normal backlog priority with limited blast radius. proof: supplied External PR includes structured after-fix real behavior proof. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. size: S status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: Gemini Context Caching Conflicts with systemInstruction and tools (PR #71441 closed without merge)

3 participants