openclaw - 💡(How to fix) Fix [Bug]: Gemini web_search freshness sends sub-second timestamps, rejected as "nano granularity" [1 pull requests]

Official PRs (…)
ON THIS PAGE

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…

Gemini web_search calls with any non-null freshness value fail with Gemini API 400 (Granularity of nano is not supported) on 2026.5.19, because the provider produces RFC 3339 timestamps that include milliseconds and Gemini's google_search.timeRangeFilter rejects sub-second precision.

Error Message

  1. Observe the Gemini 400 error in the tool output. Gemini API error (400): * GenerateContentRequest.tools[0].google_search.time_range_filter: [FIELD_INVALID] Granularity of nano is not supported [code=INVALID_ARGUMENT] "status": "error", "error": "Gemini API error (400): * GenerateContentRequest.tools[0].google_search.time_range_filter: [FIELD_INVALID] Granularity of nano is not supported [code=INVALID_ARGUMENT]" ERROR code=400 status=INVALID_ARGUMENT

Root Cause

Gemini web_search calls with any non-null freshness value fail with Gemini API 400 (Granularity of nano is not supported) on 2026.5.19, because the provider produces RFC 3339 timestamps that include milliseconds and Gemini's google_search.timeRangeFilter rejects sub-second precision.

Fix Action

Fixed

Code Example

{
  "query": "SeaTac SEA transcontinental routes 2026",
  "maxResults": 10,
  "freshness": "year"
}

---

Gemini API error (400): * GenerateContentRequest.tools[0].google_search.time_range_filter: [FIELD_INVALID] Granularity of nano is not supported [code=INVALID_ARGUMENT]

---

TOOL INPUT
{
  "query": "SeaTac SEA international long-haul routes 2026 schedule",
  "maxResults": 10,
  "freshness": "year"
}

TOOL OUTPUT
{
  "status": "error",
  "tool": "web_search",
  "error": "Gemini API error (400): * GenerateContentRequest.tools[0].google_search.time_range_filter: [FIELD_INVALID] Granularity of nano is not supported [code=INVALID_ARGUMENT]"
}

---

function freshnessStartTime(freshness, now) {
    const start = new Date(now);
    start.setUTCDate(start.getUTCDate() - GEMINI_FRESHNESS_DAYS[freshness]);
    return start.toISOString();   // → "2026-05-20T17:30:45.123Z" (millisecond precision)
}
// ...
if (freshness) return { timeRangeFilter: {
    startTime: freshnessStartTime(freshness, now),
    endTime: now.toISOString()
} };

---

=== millisecond precision ("2025-05-21T17:30:45.123Z" / "2026-05-21T17:30:45.456Z") ===
  ERROR code=400 status=INVALID_ARGUMENT
  msg: * GenerateContentRequest.tools[0].google_search.time_range_filter: [FIELD_INVALID] Granularity of nano is not supported

=== second precision ("2025-05-21T17:30:45Z" / "2026-05-21T17:30:45Z") ===
  OK candidates=1 grounded=True

---

start.toISOString().replace(/\.\d+Z$/, "Z")
RAW_BUFFERClick to expand / collapse

Bug type

Regression (worked before, now fails)

Beta release blocker

No

Summary

Gemini web_search calls with any non-null freshness value fail with Gemini API 400 (Granularity of nano is not supported) on 2026.5.19, because the provider produces RFC 3339 timestamps that include milliseconds and Gemini's google_search.timeRangeFilter rejects sub-second precision.

Steps to reproduce

  1. Run an OpenClaw 2026.5.19 gateway with the bundled Google plugin enabled and GEMINI_API_KEY set.
  2. Issue any Gemini-backed web_search with freshness set (e.g. freshness: "year").
  3. Observe the Gemini 400 error in the tool output.

Minimal tool input that reproduces it (sent by an agent during normal use):

{
  "query": "SeaTac SEA transcontinental routes 2026",
  "maxResults": 10,
  "freshness": "year"
}

Expected behavior

Per issue #66498 (closed by commit 20333bd58d and shipped in v2026.5.19), freshness is documented as supported for the Gemini provider and converts to a google_search.timeRangeFilter interval that Google accepts. Closing comment includes "Live Gemini REST smoke with google_search.timeRangeFilter returned grounded content and citations."

Actual behavior

Every Gemini web_search call that includes freshness fails with:

Gemini API error (400): * GenerateContentRequest.tools[0].google_search.time_range_filter: [FIELD_INVALID] Granularity of nano is not supported [code=INVALID_ARGUMENT]

The agent recovers on a retry with freshness omitted, so end-user impact is "extra ~14s latency and one wasted grounding quota per call" rather than a hard failure. But the documented capability is broken on every invocation.

Root cause (verified by reading the compiled bundle in the 2026.5.19 image at /app/dist/gemini-web-search-provider.runtime-CYCR53-F.js and by empirical test against the live Gemini REST API — see Logs section): freshnessStartTime returns start.toISOString(), which emits a string like 2026-05-20T17:30:45.123Z. The Gemini Search grounding endpoint rejects any fractional-second component in time_range_filter.start_time / end_time, even though Google's official Protobuf Timestamp spec — to which timeRangeFilter is documented to conform — explicitly states it accepts 0, 3, 6, or 9 fractional digits. The Search grounding path enforces a tighter rule than the underlying type. Stripping fractional seconds before serializing produces a timestamp Gemini accepts (verified end-to-end).

OpenClaw version

2026.5.19

Operating system

macOS 15 (Darwin 25.5.0) — gateway runs in the official Docker image (ghcr.io/openclaw/openclaw:2026.5.19)

Install method

docker (ghcr.io/openclaw/openclaw:2026.5.19)

Model

gemini-2.5-flash (default for the Google web_search backend; the agent's chat model was ollama/gpt-oss:120b but the failing call is in the Google plugin's grounded search path, independent of the agent model)

Provider / routing chain

openclaw-gateway → Google Gemini REST API (generateContent with tools: [{ google_search: { timeRangeFilter } }])

Additional provider/model setup details

  • tools.web.search.provider = "gemini" (set via openclaw config)
  • GEMINI_API_KEY provided via env_file; Google AI Studio paid Tier 1 (billing enabled)
  • No model override on the Google plugin's webSearch.model; defaults to gemini-2.5-flash
  • No proxies or gateways between OpenClaw and Google

Logs, screenshots, and evidence

Tool input/output captured from a real agent chat turn (Web Control UI):

TOOL INPUT
{
  "query": "SeaTac SEA international long-haul routes 2026 schedule",
  "maxResults": 10,
  "freshness": "year"
}

TOOL OUTPUT
{
  "status": "error",
  "tool": "web_search",
  "error": "Gemini API error (400): * GenerateContentRequest.tools[0].google_search.time_range_filter: [FIELD_INVALID] Granularity of nano is not supported [code=INVALID_ARGUMENT]"
}

Source-level evidence in /app/dist/gemini-web-search-provider.runtime-CYCR53-F.js:

function freshnessStartTime(freshness, now) {
    const start = new Date(now);
    start.setUTCDate(start.getUTCDate() - GEMINI_FRESHNESS_DAYS[freshness]);
    return start.toISOString();   // → "2026-05-20T17:30:45.123Z" (millisecond precision)
}
// ...
if (freshness) return { timeRangeFilter: {
    startTime: freshnessStartTime(freshness, now),
    endTime: now.toISOString()
} };

End-to-end empirical verification — two direct calls to the Gemini REST API from the same gateway container, identical request bodies except for timestamp precision:

=== millisecond precision ("2025-05-21T17:30:45.123Z" / "2026-05-21T17:30:45.456Z") ===
  ERROR code=400 status=INVALID_ARGUMENT
  msg: * GenerateContentRequest.tools[0].google_search.time_range_filter: [FIELD_INVALID] Granularity of nano is not supported

=== second precision ("2025-05-21T17:30:45Z" / "2026-05-21T17:30:45Z") ===
  OK candidates=1 grounded=True

Reference: Google's Protobuf Timestamp spec (the type timeRangeFilter.startTime / endTime are documented as) says output uses "0, 3, 6 or 9 fractional digits" and accepts the same on input — so by the type spec, the millisecond form should work. The Search grounding endpoint's behavior is stricter than the type contract.

Impact and severity

  • Affected: every OpenClaw 2026.5.19 user whose web_search agent invokes the Gemini provider with freshness, date_after, or date_before. The date_* paths use the same now.toISOString() end value and likely share the failure mode; we have only directly observed the freshness path.
  • Severity: low/medium. Annoying — wastes ~14s and one grounding quota per affected call; agents that retry without the filter recover. But it negates the entire fix shipped for #66498.
  • Frequency: every call with the affected params.
  • Consequence: wasted Gemini grounding quota, slower turn latency, and (more importantly) freshness-filtered searches return no value because they always fail. Time-range filtering on Gemini web_search is effectively unusable in this release.

Additional information

  • Regression scope: there is no last-known-good version — the feature was first shipped in 2026.5.19 (commit 20333bd58d), and as far as I can tell has never worked end-to-end. Closing the loop on #66498 included a live smoke test that returned content, which suggests something about how the smoke test was constructed differed from a realistic agent call — but I haven't reproduced the smoke test path to confirm.

  • Verified small fix: in freshnessStartTime and at both endTime: now.toISOString() call sites, strip fractional seconds before serializing. The pattern below produces a timestamp the live Gemini API accepts (confirmed in the empirical test above):

    start.toISOString().replace(/\.\d+Z$/, "Z")
  • Happy to send a PR if this analysis aligns with how the maintainers want to handle it. Wanted to surface the regression with a clean repro first.

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…

FAQ

Expected behavior

Per issue #66498 (closed by commit 20333bd58d and shipped in v2026.5.19), freshness is documented as supported for the Gemini provider and converts to a google_search.timeRangeFilter interval that Google accepts. Closing comment includes "Live Gemini REST smoke with google_search.timeRangeFilter returned grounded content and citations."

Still need to ship something?

×6

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

Back to top recommendations

TRENDING