openclaw - 💡(How to fix) Fix Gemini image generation always fails with 'Headers Timeout Error' — per-request undici Agent doesn't inherit timeouts [2 comments, 1 participants]

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…
GitHub stats
openclaw/openclaw#70423Fetched 2026-04-23 07:24:55
View on GitHub
Comments
2
Participants
1
Timeline
2
Reactions
0
Author
Participants
Timeline (top)
commented ×2

When invoking the built-in image_generate tool with any Google Gemini image model (google/gemini-3-pro-image-preview or google/gemini-3.1-flash-image-preview), the request consistently fails inside the gateway runtime fetch path with:

[image-generation] candidate failed: google/gemini-3-pro-image-preview:
fetch failed | Headers Timeout Error

…even though the same API call from curl and stand-alone Node fetch succeeds in 16–20s with a 3–4 MB response body. As a result, every Gemini image generation falls back to the secondary provider (e.g. openai/gpt-image-2).


Error Message

2026-04-23T08:04:30.684+07:00 [image-generation] candidate failed:
  google/gemini-3-pro-image-preview: fetch failed | Headers Timeout Error
2026-04-23T08:09:10.195+07:00 [image-generation] candidate failed:
  google/gemini-3-pro-image-preview: fetch failed | Headers Timeout Error
2026-04-23T08:09:47.029+07:00 [image-generation] candidate failed:
  google/gemini-3.1-flash-image-preview: fetch failed | Headers Timeout Error
2026-04-23T08:10:04.290+07:00 [image-generation] candidate failed:
  google/gemini-3-pro-image-preview: fetch failed | Headers Timeout Error
2026-04-23T08:11:58.749+07:00 [image-generation] candidate failed:
  google/gemini-3-pro-image-preview: fetch failed | Headers Timeout Error

(All correlate with successful curl / node fetch runs against the same endpoint, same key, same machine.)


Root Cause

Root cause analysis (best guess)

Fix Action

Fix / Workaround

→ The upstream is healthy. The failure is inside the gateway’s guarded fetch / runtime dispatcher path.

postJsonRequest({
  url: `${baseUrl}/models/${model}:generateContent`,
  headers,
  body,
  timeoutMs: 6e4,        // 60s
  fetchFn: fetch,
  pinDns: false,
  allowPrivateNetwork,
  dispatcherPolicy
})

The chain is: postJsonRequestfetchWithTimeoutGuardedfetchWithSsrFGuardfetchWithRuntimeDispatcher (in runtime-fetch-*.js) which uses createHttp1Agent from dist/undici-runtime-CF8Z6FKd.js:

Code Example

[image-generation] candidate failed: google/gemini-3-pro-image-preview:
fetch failed | Headers Timeout Error

---

"agents": {
     "defaults": {
       "imageGenerationModel": {
         "primary": "google/gemini-3-pro-image-preview",
         "fallbacks": ["openai/gpt-image-2"]
       }
     }
   }

---

ImageCreate(
  prompt="A simple yellow lemon",
  model="google/gemini-3-pro-image-preview",
  size="1024x1024"
)

---

[image-generation] candidate failed: google/gemini-3-pro-image-preview:
  fetch failed | Headers Timeout Error

---

curl -sS -m 60 -o /tmp/gem-direct.json \
  -w "http=%{http_code} time=%{time_total}s size=%{size_download}\n" \
  "https://generativelanguage.googleapis.com/v1beta/models/gemini-3-pro-image-preview:generateContent?key=$GKEY" \
  -H "Content-Type: application/json" \
  -d '{"contents":[{"role":"user","parts":[{"text":"a yellow lemon"}]}],
       "generationConfig":{"responseModalities":["TEXT","IMAGE"]}}'

# Result:
# http=200 time=19.94s size=3017483

---

fetch(url, { method: "POST", headers, body }) // ~16s, HTTP 200, 4 MB

---

postJsonRequest({
  url: `${baseUrl}/models/${model}:generateContent`,
  headers,
  body,
  timeoutMs: 6e4,        // 60s
  fetchFn: fetch,
  pinDns: false,
  allowPrivateNetwork,
  dispatcherPolicy
})

---

function createHttp1Agent(options) {
  const { Agent } = loadUndiciRuntimeDeps();
  return new Agent(withHttp1OnlyDispatcherOptions(options));
}
const HTTP1_ONLY_DISPATCHER_OPTIONS = Object.freeze({ allowH2: false });

---

function createHttp1Agent(options) {
  const { Agent } = loadUndiciRuntimeDeps();
  return new Agent({
    headersTimeout: options?.headersTimeout ?? DEFAULT_UNDICI_STREAM_TIMEOUT_MS,
    bodyTimeout:    options?.bodyTimeout    ?? DEFAULT_UNDICI_STREAM_TIMEOUT_MS,
    ...withHttp1OnlyDispatcherOptions(options),
  });
}

---

- timeoutMs: 6e4,
+ timeoutMs: 1.8e5,   // 180s — matches OpenAI image gen path

---

2026-04-23T08:04:30.684+07:00 [image-generation] candidate failed:
  google/gemini-3-pro-image-preview: fetch failed | Headers Timeout Error
2026-04-23T08:09:10.195+07:00 [image-generation] candidate failed:
  google/gemini-3-pro-image-preview: fetch failed | Headers Timeout Error
2026-04-23T08:09:47.029+07:00 [image-generation] candidate failed:
  google/gemini-3.1-flash-image-preview: fetch failed | Headers Timeout Error
2026-04-23T08:10:04.290+07:00 [image-generation] candidate failed:
  google/gemini-3-pro-image-preview: fetch failed | Headers Timeout Error
2026-04-23T08:11:58.749+07:00 [image-generation] candidate failed:
  google/gemini-3-pro-image-preview: fetch failed | Headers Timeout Error
RAW_BUFFERClick to expand / collapse

Bug Report: Gemini image generation always fails with Headers Timeout Error despite direct API responding in <20s

Reporter: Giang Tran Date: 2026-04-23 OpenClaw version: 2026.4.21 Node: v24.14.1 OS: WSL2 on Windows 11 (6.6.87.2-microsoft-standard-WSL2) Gateway PID at repro time: 223116


Summary

When invoking the built-in image_generate tool with any Google Gemini image model (google/gemini-3-pro-image-preview or google/gemini-3.1-flash-image-preview), the request consistently fails inside the gateway runtime fetch path with:

[image-generation] candidate failed: google/gemini-3-pro-image-preview:
fetch failed | Headers Timeout Error

…even though the same API call from curl and stand-alone Node fetch succeeds in 16–20s with a 3–4 MB response body. As a result, every Gemini image generation falls back to the secondary provider (e.g. openai/gpt-image-2).


Impact

  • Default agents.defaults.imageGenerationModel.primary = "google/gemini-3-pro-image-preview" is effectively unusable.
  • Any agent or tool that relies on Gemini image gen silently degrades to the fallback provider.
  • Users see misleading output like Generated 1 image with openai/gpt-image-2 when they explicitly requested a Google model.

Reproduction

Setup

  1. Configure a working Google Gemini API key under models.providers.gemini.apiKey.
  2. Configure default image gen:
    "agents": {
      "defaults": {
        "imageGenerationModel": {
          "primary": "google/gemini-3-pro-image-preview",
          "fallbacks": ["openai/gpt-image-2"]
        }
      }
    }
  3. Restart gateway.

Trigger

Call the image_generate tool from any agent context:

ImageCreate(
  prompt="A simple yellow lemon",
  model="google/gemini-3-pro-image-preview",
  size="1024x1024"
)

Observed

  • Tool returns Generated 1 image with openai/gpt-image-2 (fallback).
  • Gateway log:
    [image-generation] candidate failed: google/gemini-3-pro-image-preview:
    fetch failed | Headers Timeout Error
  • This happens 100% of the time for gemini-3-pro-image-preview and gemini-3.1-flash-image-preview.

Expected

  • Gateway should successfully receive the Gemini response (which the provider does return in ~19s) and emit a Gemini-generated image.

Evidence: direct API call works fine

curl -sS -m 60 -o /tmp/gem-direct.json \
  -w "http=%{http_code} time=%{time_total}s size=%{size_download}\n" \
  "https://generativelanguage.googleapis.com/v1beta/models/gemini-3-pro-image-preview:generateContent?key=$GKEY" \
  -H "Content-Type: application/json" \
  -d '{"contents":[{"role":"user","parts":[{"text":"a yellow lemon"}]}],
       "generationConfig":{"responseModalities":["TEXT","IMAGE"]}}'

# Result:
# http=200 time=19.94s size=3017483

Stand-alone Node fetch() from the same machine, same network, same key:

fetch(url, { method: "POST", headers, body }) // ~16s, HTTP 200, 4 MB

→ The upstream is healthy. The failure is inside the gateway’s guarded fetch / runtime dispatcher path.


Root cause analysis (best guess)

dist/extensions/google/image-generation-provider.js calls:

postJsonRequest({
  url: `${baseUrl}/models/${model}:generateContent`,
  headers,
  body,
  timeoutMs: 6e4,        // 60s
  fetchFn: fetch,
  pinDns: false,
  allowPrivateNetwork,
  dispatcherPolicy
})

The chain is: postJsonRequestfetchWithTimeoutGuardedfetchWithSsrFGuardfetchWithRuntimeDispatcher (in runtime-fetch-*.js) which uses createHttp1Agent from dist/undici-runtime-CF8Z6FKd.js:

function createHttp1Agent(options) {
  const { Agent } = loadUndiciRuntimeDeps();
  return new Agent(withHttp1OnlyDispatcherOptions(options));
}
const HTTP1_ONLY_DISPATCHER_OPTIONS = Object.freeze({ allowH2: false });

This per-request HTTP/1 Agent does not propagate headersTimeout / bodyTimeout from the global undici dispatcher (which is set to 1800 * 1000ms by ensureGlobalUndiciStreamTimeouts in dist/undici-global-dispatcher-NJHeJfvr.js).

The new Agent inherits undici defaults (300s headersTimeout / 300s bodyTimeout in newer undici, but in some versions / under WSL2 connect:autoSelectFamily=false paths the effective wait is shorter). For Gemini image responses that take ~16-20s with large bodies, this trips “Headers Timeout Error”, surfaced via undici’s UND_ERR_HEADERS_TIMEOUT cause.

The 60s timeoutMs from postJsonRequest only controls the outer AbortController (buildTimeoutAbortSignal) — it does not override the dispatcher-level headersTimeout.


Suggested fix

Option A (preferred): propagate timeouts into per-request agent

Make createHttp1Agent, createHttp1EnvHttpProxyAgent, createHttp1ProxyAgent accept (or default) headersTimeout and bodyTimeout derived from the caller’s timeoutMs (or from DEFAULT_UNDICI_STREAM_TIMEOUT_MS):

function createHttp1Agent(options) {
  const { Agent } = loadUndiciRuntimeDeps();
  return new Agent({
    headersTimeout: options?.headersTimeout ?? DEFAULT_UNDICI_STREAM_TIMEOUT_MS,
    bodyTimeout:    options?.bodyTimeout    ?? DEFAULT_UNDICI_STREAM_TIMEOUT_MS,
    ...withHttp1OnlyDispatcherOptions(options),
  });
}

…and let fetchWithRuntimeDispatcher pass these through.

Option B: bump the image gen timeout

Even with Option A, image-gen providers should request a longer outer timeout. In dist/extensions/google/image-generation-provider.js:

- timeoutMs: 6e4,
+ timeoutMs: 1.8e5,   // 180s — matches OpenAI image gen path

Option C: surface the real cause

In gateway logs, expand Headers Timeout Error to include:

  • effective headersTimeout and bodyTimeout of the dispatcher used,
  • elapsed ms before abort,
  • whether the abort came from the outer AbortController or from undici.

This would make future timeout bugs much faster to triage.


Workarounds tried

  1. Swap fallback order (openai/gpt-image-2 primary, google/... fallback) — works, but loses Gemini quality for the primary path.
  2. Explicit model override to Gemini — same failure.
  3. Patch image-generation-provider.js timeoutMs locally to 180000 — partial fix; still hits dispatcher headersTimeout in our environment.

Environment matrix

ComponentValue
OpenClaw2026.4.21
Nodev24.14.1
OSWindows 11 + WSL2 (6.6.87.2-microsoft-standard-WSL2)
NetworkVietnam, residential, no proxy, no VPN
Direct curl latency to generativelanguage.googleapis.com~20s for image gen
Direct Node fetch latency~16s, HTTP 200, 4 MB body
Gateway-side latency before failure< 1s (fails fast)

Logs

2026-04-23T08:04:30.684+07:00 [image-generation] candidate failed:
  google/gemini-3-pro-image-preview: fetch failed | Headers Timeout Error
2026-04-23T08:09:10.195+07:00 [image-generation] candidate failed:
  google/gemini-3-pro-image-preview: fetch failed | Headers Timeout Error
2026-04-23T08:09:47.029+07:00 [image-generation] candidate failed:
  google/gemini-3.1-flash-image-preview: fetch failed | Headers Timeout Error
2026-04-23T08:10:04.290+07:00 [image-generation] candidate failed:
  google/gemini-3-pro-image-preview: fetch failed | Headers Timeout Error
2026-04-23T08:11:58.749+07:00 [image-generation] candidate failed:
  google/gemini-3-pro-image-preview: fetch failed | Headers Timeout Error

(All correlate with successful curl / node fetch runs against the same endpoint, same key, same machine.)


Files touched during investigation

  • dist/extensions/google/image-generation-provider.js
  • dist/extensions/google/model-id.js
  • dist/undici-global-dispatcher-NJHeJfvr.js
  • dist/undici-runtime-CF8Z6FKd.js
  • dist/fetch-timeout-BnJMd7ko.js
  • dist/shared-DShjzMyJ.js
  • dist/runtime-DeKfuvWV.js

Severity

High for users who rely on Gemini image generation as primary — silently routes to a different provider (and a different bill) without any user-facing indication.

Low for users who already use OpenAI / other providers — purely cosmetic if they don’t notice the model swap in tool output.

extent analysis

TL;DR

The most likely fix for the Gemini image generation failure is to propagate timeouts into the per-request agent by modifying the createHttp1Agent function to accept and apply headersTimeout and bodyTimeout derived from the caller's timeoutMs.

Guidance

  1. Modify createHttp1Agent: Update the function to accept headersTimeout and bodyTimeout options and apply them to the new Agent instance.
  2. Pass timeouts through fetchWithRuntimeDispatcher: Ensure that fetchWithRuntimeDispatcher passes the headersTimeout and bodyTimeout options to createHttp1Agent.
  3. Increase image generation timeout: Consider increasing the timeoutMs value in image-generation-provider.js to a higher value, such as 180s, to match the OpenAI image generation path.
  4. Verify the fix: Test the modified code with the Gemini image generation tool to ensure that it no longer fails with a "Headers Timeout Error".

Example

function createHttp1Agent(options) {
  const { Agent } = loadUndiciRuntimeDeps();
  return new Agent({
    headersTimeout: options?.headersTimeout ?? DEFAULT_UNDICI_STREAM_TIMEOUT_MS,
    bodyTimeout:    options?.bodyTimeout    ?? DEFAULT_UNDICI_STREAM_TIMEOUT_MS,
    ...withHttp1OnlyDispatcherOptions(options),
  });
}

Notes

The provided solution assumes that the issue is caused by the per-request HTTP/1 Agent not propagating headersTimeout and bodyTimeout from the global undici dispatcher. If the issue persists after applying this fix, further investigation may be necessary to identify the root cause.

Recommendation

Apply the workaround by modifying the createHttp1Agent function to propagate timeouts, as this is the most likely cause of the issue and has a clear solution.

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

openclaw - 💡(How to fix) Fix Gemini image generation always fails with 'Headers Timeout Error' — per-request undici Agent doesn't inherit timeouts [2 comments, 1 participants]