openclaw - 💡(How to fix) Fix Bug: Gemini Context Caching Conflicts with systemInstruction and tools (PR #71441 closed without merge)

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

Error Message

When cacheRetention: "long" is configured for a Gemini model with tools, the OpenClaw gateway sends cachedContent, systemInstruction, AND tools/toolConfig in the same request. The Google Gemini API rejects this combination with error 400: Google Generative AI API error (400): CachedContent can not be used with GenerateContent request setting system_instruction, tools or tool_config. 4. Observe error 400 from Google API

Root Cause

The code in transport-stream-DyvRcab3.js (function buildGoogleGenerativeAiParams) sends all fields unconditionally:

if (typeof options?.cachedContent === "string" && options.cachedContent.trim()) 
    params.cachedContent = options.cachedContent.trim();
if (context.systemPrompt) 
    params.systemInstruction = { parts: [{ text: sanitizeTransportPayloadText(stripSystemPromptCacheBoundary(context.systemPrompt)) }] };
if (context.tools?.length) {
    params.tools = convertGoogleTools(context.tools);
    const toolChoice = mapToolChoice(options?.toolChoice);
    if (toolChoice) params.toolConfig = { functionCallingConfig: toolChoice };
}

According to Google's API documentation, when cachedContent is used, the systemInstruction, tools, and toolConfig must NOT be sent separately — they should already be embedded in the cached content.

Fix Action

Fix / Workaround

Workaround: Remove cacheRetention from all Gemini model configs:

{
  "google/gemini-3.5-flash": {}  // no cacheRetention
}

Code Example

Google Generative AI API error (400): CachedContent can not be used with GenerateContent request setting system_instruction, tools or tool_config.

---

if (typeof options?.cachedContent === "string" && options.cachedContent.trim()) 
    params.cachedContent = options.cachedContent.trim();
if (context.systemPrompt) 
    params.systemInstruction = { parts: [{ text: sanitizeTransportPayloadText(stripSystemPromptCacheBoundary(context.systemPrompt)) }] };
if (context.tools?.length) {
    params.tools = convertGoogleTools(context.tools);
    const toolChoice = mapToolChoice(options?.toolChoice);
    if (toolChoice) params.toolConfig = { functionCallingConfig: toolChoice };
}

---

{
  "google/gemini-3.5-flash": {}  // no cacheRetention
}

---

const usesCachedContent = typeof options?.cachedContent === "string" && options.cachedContent.trim();
if (usesCachedContent) params.cachedContent = options.cachedContent.trim();
if (!usesCachedContent && context.systemPrompt) 
    params.systemInstruction = { parts: [{ text: sanitizeTransportPayloadText(stripSystemPromptCacheBoundary(context.systemPrompt)) }] };
if (!usesCachedContent && context.tools?.length) {
    params.tools = convertGoogleTools(context.tools);
    const toolChoice = mapToolChoice(options?.toolChoice);
    if (toolChoice) params.toolConfig = { functionCallingConfig: toolChoice };
}
RAW_BUFFERClick to expand / collapse

Bug: Gemini Context Caching Conflicts with systemInstruction and tools

Problem

When cacheRetention: "long" is configured for a Gemini model with tools, the OpenClaw gateway sends cachedContent, systemInstruction, AND tools/toolConfig in the same request. The Google Gemini API rejects this combination with error 400:

Google Generative AI API error (400): CachedContent can not be used with GenerateContent request setting system_instruction, tools or tool_config.

Root Cause

The code in transport-stream-DyvRcab3.js (function buildGoogleGenerativeAiParams) sends all fields unconditionally:

if (typeof options?.cachedContent === "string" && options.cachedContent.trim()) 
    params.cachedContent = options.cachedContent.trim();
if (context.systemPrompt) 
    params.systemInstruction = { parts: [{ text: sanitizeTransportPayloadText(stripSystemPromptCacheBoundary(context.systemPrompt)) }] };
if (context.tools?.length) {
    params.tools = convertGoogleTools(context.tools);
    const toolChoice = mapToolChoice(options?.toolChoice);
    if (toolChoice) params.toolConfig = { functionCallingConfig: toolChoice };
}

According to Google's API documentation, when cachedContent is used, the systemInstruction, tools, and toolConfig must NOT be sent separately — they should already be embedded in the cached content.

Previous Work

PR #71441 implemented the correct fix:

  • Add usesCachedContent guard before setting systemInstruction and tools/toolConfig
  • The PR was reviewed and approved (Confidence Score: 5/5)
  • However, PR #71441 was CLOSED WITHOUT MERGE on 2026-04-25

This means the fix exists but was never applied to the codebase.

Impact

Workaround: Remove cacheRetention from all Gemini model configs:

{
  "google/gemini-3.5-flash": {}  // no cacheRetention
}

Cost Impact: Without context caching, users pay 100% of input token costs instead of ~25% (cache read rate). For agents with large system prompts (~35KB), this represents significant cost overhead.

Reproduction

  1. Configure a Gemini model with cacheRetention: "long" and tools
  2. Create an agent with a large system prompt (SOUL.md, TOOLS.md, etc.)
  3. Send a message to the agent
  4. Observe error 400 from Google API

Environment

  • OpenClaw version: 2026.5.19 (a185ca2)
  • Model: google/gemini-3.5-flash (also affects gemini-3.1-flash-lite, gemini-2.5-flash-lite, gemini-2.0-flash)
  • Config: cacheRetention: "long" + agent with tools

Related

  • Feature request: #51372 (Gemini Context Caching Support)
  • Closed PR with fix: #71441

Suggested Fix

Re-open and merge PR #71441, or apply equivalent fix:

const usesCachedContent = typeof options?.cachedContent === "string" && options.cachedContent.trim();
if (usesCachedContent) params.cachedContent = options.cachedContent.trim();
if (!usesCachedContent && context.systemPrompt) 
    params.systemInstruction = { parts: [{ text: sanitizeTransportPayloadText(stripSystemPromptCacheBoundary(context.systemPrompt)) }] };
if (!usesCachedContent && context.tools?.length) {
    params.tools = convertGoogleTools(context.tools);
    const toolChoice = mapToolChoice(options?.toolChoice);
    if (toolChoice) params.toolConfig = { functionCallingConfig: toolChoice };
}

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING