Skip to content

fix: propagate timeoutMs to guarded dispatchers (local LLM 60s timeout)#70831

Merged
steipete merged 6 commits into
openclaw:mainfrom
DranboFieldston:fix/openclaw-timeout-propagation
Apr 24, 2026
Merged

fix: propagate timeoutMs to guarded dispatchers (local LLM 60s timeout)#70831
steipete merged 6 commits into
openclaw:mainfrom
DranboFieldston:fix/openclaw-timeout-propagation

Conversation

@DranboFieldston

Copy link
Copy Markdown
Contributor

Problem

Local LLM requests via openai-completions (and other provider transports) are killed at exactly 60 seconds, despite having timeoutSeconds: 900 (15 min) configured in openclaw.json.

Root Cause

Timeout propagation gap in the guarded fetch path.

OpenClaw correctly sets a global undici dispatcher timeout via ensureGlobalUndiciStreamTimeouts() in attempt-http-runtime.ts. However, the LLM transport path (openai-transport-stream.ts, anthropic-transport-stream.ts) uses buildGuardedModelFetch(), which calls fetchWithSsrFGuard().

fetchWithSsrFGuard() creates isolated dispatchers per-request for SSRF security (DNS pinning, private network isolation). These isolated dispatchers call createHttp1Agent(), createHttp1EnvHttpProxyAgent(), createHttp1ProxyAgent(), or createPinnedDispatcher() from undici-runtime.ts — and none of them currently accept or apply the timeoutMs parameter. They fall back to undici's hardcoded default of 60s for bodyTimeout and headersTimeout.

The 15-minute AbortSignal is correctly created (via buildTimeoutAbortSignal()), but undici kills the connection at 60s before the AbortSignal fires at 15 min.

The Flow

```
openclaw.json → timeoutSeconds: 900 (15 min)

attempt.ts → params.timeoutMs = 900000

attempt-http-runtime.ts → ensureGlobalUndiciStreamTimeouts({ timeoutMs }) ✅ (global dispatcher, 15 min)

openai-transport-stream.ts → buildGuardedModelFetch(model) ❌ (no timeoutMs passed)

provider-transport-fetch.ts → fetchWithSsrFGuard({ ..., timeoutMs: undefined }) ❌ (passed to AbortSignal only)

fetch-guard.ts → createHttp1Agent() / createPinnedDispatcher() ❌ (no timeoutMs)

undici-runtime.ts → new Agent() with no bodyTimeout/headersTimeout → undici default 60s ❌
```

Investigation

Proposed Fix

Thread `timeoutMs` through the dispatcher creation chain:

  1. `src/infra/net/undici-runtime.ts`: `createHttp1Agent()`, `createHttp1EnvHttpProxyAgent()`, `createHttp1ProxyAgent()` accept `timeoutMs` and apply `bodyTimeout`/`headersTimeout` to dispatcher options.
  2. `src/infra/net/ssrf.ts`: `createPinnedDispatcher()` accepts `timeoutMs` and passes it through to HTTP/1 dispatcher creation.
  3. `src/infra/net/fetch-guard.ts`: `fetchWithSsrFGuard()` reads timeout from `params.timeoutMs` or falls back to the global dispatcher's `bodyTimeout` via `getGlobalDispatcher()`. All dispatcher creation calls receive the timeout value.
  4. `src/agents/provider-transport-fetch.ts`: `buildGuardedModelFetch()` accepts optional `timeoutMs` and passes it to `fetchWithSsrFGuard()`.

Minimal Patch (4 files)

`src/infra/net/undici-runtime.ts`

  • `withHttp1OnlyDispatcherOptions()`: now accepts `timeoutMs`, applies `bodyTimeout`/headersTimeout to dispatcher options
  • `createHttp1Agent()`, `createHttp1EnvHttpProxyAgent()`, `createHttp1ProxyAgent()`: all accept `timeoutMs` parameter and pass it through

`src/infra/net/ssrf.ts`

  • `createPinnedDispatcher()`: added `timeoutMs?: number` as 4th parameter, passes it to all dispatcher creation calls

`src/infra/net/fetch-guard.ts`

  • Added `resolveDispatcherTimeoutMs()` helper: reads from `params.timeoutMs` if provided, otherwise reads `bodyTimeout` from the global dispatcher via `getGlobalDispatcher()`
  • `createPolicyDispatcherWithoutPinnedDns()`: accepts `timeoutMs`, passes to dispatcher creation
  • `fetchWithSsrFGuard()`: computes `timeoutMs` via `resolveDispatcherTimeoutMs()` before dispatcher creation

`src/agents/provider-transport-fetch.ts`

  • `buildGuardedModelFetch()`: accepts optional `timeoutMs` as 2nd parameter, passes it to `fetchWithSsrFGuard()`

Risks & Considerations

  • Higher timeouts mean connections stay open longer (expected for slow LLM operations)
  • All changes are additive (optional parameters), fully backward compatible
  • No changes to public API surface
  • SSRF protection (isolated dispatchers) is preserved — only timeout values changed

Verification

  • ✅ Build passes: `pnpm run build`
  • ✅ All 389 test files pass, 4128 tests pass
  • ✅ TypeScript core typecheck passes
  • ✅ Lint passes (0 warnings, 0 errors)
  • ✅ Import cycles pass

Fixes #70829

@openclaw-barnacle openclaw-barnacle Bot added agents Agent runtime and tooling size: S labels Apr 23, 2026
@DranboFieldston
DranboFieldston force-pushed the fix/openclaw-timeout-propagation branch from 97a63f4 to 21feabb Compare April 23, 2026 23:20
@greptile-apps

greptile-apps Bot commented Apr 23, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR threads timeoutMs through the guarded fetch dispatcher creation chain so that per-request undici dispatchers in fetchWithSsrFGuard honour the operator-configured timeout instead of falling back to undici's hardcoded 60 s default. The primary mechanism is a new module-level bridge (_globalUndiciStreamTimeoutMs) in undici-global-dispatcher.ts that resolveDispatcherTimeoutMs in fetch-guard.ts reads as a fallback when no explicit per-call timeout is provided — meaning no changes to the existing transport-stream callers (openai-transport-stream.ts, anthropic-transport-stream.ts) are required.

Confidence Score: 5/5

Safe to merge — the fix is correct, all changes are additive and backward compatible, and SSRF protection is unaffected.

The only open finding is a P2 style inconsistency in createHttp1ProxyAgent (bypasses the shared withHttp1OnlyDispatcherOptions helper). The core timeout propagation logic is sound: _globalUndiciStreamTimeoutMs is set inside ensureGlobalUndiciStreamTimeouts, read as a fallback in resolveDispatcherTimeoutMs, and correctly applied to all four dispatcher creation paths. No P0/P1 issues remain.

src/infra/net/undici-runtime.ts — minor inconsistency in createHttp1ProxyAgent (inlines logic that the other two factory functions delegate to withHttp1OnlyDispatcherOptions).

Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/infra/net/undici-runtime.ts
Line: 87-103

Comment:
**`createHttp1ProxyAgent` bypasses the shared helper**

`createHttp1Agent` and `createHttp1EnvHttpProxyAgent` both delegate to `withHttp1OnlyDispatcherOptions`, which centralises the `allowH2: false` invariant and timeout logic. `createHttp1ProxyAgent` was refactored to inline those properties manually instead of calling the helper. If anything is added to `withHttp1OnlyDispatcherOptions` in the future (e.g. a new invariant beyond `allowH2`), `createHttp1ProxyAgent` will silently miss it.

Consider keeping the same shape as the other two functions:

```ts
export function createHttp1ProxyAgent(
  options: UndiciProxyAgentOptions,
  timeoutMs?: number,
): import("undici").ProxyAgent {
  const { ProxyAgent } = loadUndiciRuntimeDeps();
  const normalised =
    typeof options === "string" || options instanceof URL
      ? { uri: options.toString() }
      : { ...options };
  return new ProxyAgent(
    withHttp1OnlyDispatcherOptions(normalised as UndiciAgentOptions, timeoutMs) as UndiciProxyAgentOptions,
  );
}
```

How can I resolve this? If you propose a fix, please make it concise.

Reviews (2): Last reviewed commit: "chore: re-run CI smoke tests" | Re-trigger Greptile

Comment thread src/infra/net/undici-runtime.ts Outdated
@DranboFieldston

Copy link
Copy Markdown
Contributor Author

Gemini Investigation Report

Generated by Gemini on 2026-04-23 as part of the timeout propagation investigation.


Original report file: `openclaw-gemini-report.md`


Summary

Local LLM requests via `openai-completions` are being killed at exactly 60 seconds despite a configured 15-minute timeout in `openclaw.json`. The investigation confirms that while the 15-minute timeout is correctly applied to an `AbortSignal`, it is not passed to the underlying Undici dispatchers (Agent, ProxyAgent, etc.). Undici's default `headersTimeout` and `bodyTimeout` are 60 seconds, which causes the connection to be killed before the `AbortSignal` fires.


File-by-File Analysis

1. `src/agents/provider-transport-fetch.ts`

  • Status: Incorrectly handles `timeoutMs` (implicitly drops it).
  • Findings: `buildGuardedModelFetch()` creates a fetch function that calls `fetchWithSsrFGuard()`. However, it only passes `init` (which might contain a `signal`), but it does not explicitly pass a `timeoutMs` property from the model configuration to `fetchWithSsrFGuard()`.
  • Exact Line(s): L121-137 — The `fetchWithSsrFGuard` call is missing the `timeoutMs` parameter.

2. `src/infra/net/fetch-guard.ts`

  • Status: Correctly handles `timeoutMs` for `AbortSignal`, but fails to pass it to the dispatcher.
  • Findings: `fetchWithSsrFGuard()` accepts `timeoutMs` and uses it to create a `signal` via `buildTimeoutAbortSignal()`. However, it does not pass `timeoutMs` to `createPinnedDispatcher()` or `createHttp1EnvHttpProxyAgent()`.
  • Exact Line(s): L256 — `dispatcher = createPinnedDispatcher(pinned, params.dispatcherPolicy, params.policy);` (Missing `timeoutMs`).

3. `src/infra/net/undici-runtime.ts`

  • Status: Does not handle timeout options.
  • Findings: `createHttp1Agent()`, `createHttp1EnvHttpProxyAgent()`, and `createHttp1ProxyAgent()` all use `withHttp1OnlyDispatcherOptions()`, which only sets `allowH2: false`. They do not accept or set `bodyTimeout` or `headersTimeout`.
  • Exact Line(s): L64-67 — `createHttp1Agent` ignores any timeout-related options.

4. `src/infra/net/undici-global-dispatcher.ts`

  • Status: Correct for GLOBAL dispatcher, but irrelevant for GUARDED requests.
  • Findings: `ensureGlobalUndiciStreamTimeouts()` correctly sets `bodyTimeout` and `headersTimeout` on the global dispatcher. However, `fetchWithSsrFGuard` (used by LLM transports) frequently creates new dispatchers for DNS pinning and SSRF protection, which bypass the global dispatcher settings.

5. `src/agents/pi-embedded-runner/run/attempt-http-runtime.ts`

  • Status: Correct for embedded runner, but irrelevant for `openai-completions`.
  • Findings: `configureEmbeddedAttemptHttpRuntime()` calls `ensureGlobalUndiciStreamTimeouts()`, but this only affects the global dispatcher.

6. `src/infra/net/ssrf.ts`

  • Status: Does not accept or pass timeout options.
  • Findings: `createPinnedDispatcher()` does not have a parameter for timeout and thus cannot pass it to the `createHttp1*` functions.
  • Exact Line(s): L364 — `export function createPinnedDispatcher(pinned: PinnedHostname, policy?: PinnedDispatcherPolicy, ssrfPolicy?: SsrFPolicy): Dispatcher` (Missing `timeoutMs` in signature).

7. `src/agents/openai-transport-stream.ts`

  • Status: Correctly passes `signal`, but `timeoutMs` is missing.
  • Findings: `createOpenAICompletionsClient()` uses `buildGuardedModelFetch(model)`. The OpenAI SDK passes the `signal` from options, which reaches `fetchWithSsrFGuard`, but the underlying dispatcher's internal timeouts remain at 60s.

8. `src/utils/fetch-timeout.ts` (referenced as `src/infra/net/fetch-timeout.ts`)

  • Status: Correctly handles `AbortSignal`.
  • Findings: `buildTimeoutAbortSignal()` works as intended for creating a timeout-backed `AbortSignal`.

Exact Point of Failure

The timeout is lost at `src/infra/net/fetch-guard.ts` and `src/infra/net/ssrf.ts` because they do not propagate the `timeoutMs` to the Undici `Agent` constructor. This leads to Undici using its internal default of 60,000ms for `headersTimeout` and `bodyTimeout`.


Minimal Fix Proposal

  1. Modify `src/infra/net/undici-runtime.ts`: Update `createHttp1Agent`, `createHttp1EnvHttpProxyAgent`, and `createHttp1ProxyAgent` to accept and apply `bodyTimeout` and `headersTimeout`.
  2. Modify `src/infra/net/ssrf.ts`: Update `createPinnedDispatcher` to accept `timeoutMs` and pass it to the agent creation functions.
  3. Modify `src/infra/net/fetch-guard.ts`: Pass `params.timeoutMs` into `createPinnedDispatcher` and `createHttp1EnvHttpProxyAgent`.
  4. Modify `src/agents/provider-transport-fetch.ts`: Ensure `timeoutMs` is extracted from the model policy and passed to `fetchWithSsrFGuard`.

Side Effects / Concerns

  • Connection Pooling: If dispatchers are reused, changing timeouts might affect other requests using the same dispatcher. However, `fetchWithSsrFGuard` generally creates pinned dispatchers per-request or per-hostname.
  • Memory: Higher timeouts mean connections stay open longer in case of slow servers, increasing the number of concurrent open sockets. Given this is for LLMs (which are slow), this is expected behavior.

@DranboFieldston
DranboFieldston force-pushed the fix/openclaw-timeout-propagation branch from d575217 to c76e8c0 Compare April 23, 2026 23:51

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d5752175a2

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/infra/net/fetch-guard.ts Outdated
Comment on lines +37 to +39
const opts = (globalDispatcher as { options?: Record<string, unknown> }).options;
if (opts?.bodyTimeout) {
return typeof opts.bodyTimeout === "number" ? opts.bodyTimeout : undefined;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Resolve fallback timeout without relying on .options

resolveDispatcherTimeoutMs reads getGlobalDispatcher().options.bodyTimeout, but Undici’s Agent/EnvHttpProxyAgent do not expose their constructor options through a public .options field, so this branch returns undefined in normal runtime. In the provider transport path, buildGuardedModelFetch is still commonly called without an explicit timeoutMs, so guarded dispatchers continue to be created without bodyTimeout/headersTimeout and requests can still terminate at Undici’s ~60s defaults instead of the configured attempt timeout.

Useful? React with 👍 / 👎.

@DranboFieldston
DranboFieldston force-pushed the fix/openclaw-timeout-propagation branch from c76e8c0 to cacd9fb Compare April 23, 2026 23:54
@DranboFieldston

Copy link
Copy Markdown
Contributor Author

Updated with bridge variable fix for the \.options fallback — Greptile/Codex: please re-review the latest commit.

@DranboFieldston

Copy link
Copy Markdown
Contributor Author

@codex review

@DranboFieldston

Copy link
Copy Markdown
Contributor Author

/retrigger greptile

@chatgpt-codex-connector

Copy link
Copy Markdown
Contributor

Codex Review: Didn't find any major issues. What shall we delve into next?

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@DranboFieldston

Copy link
Copy Markdown
Contributor Author

CI re-run passed (all green). @greptile-apps / @codex please re-review the bridge variable fix in .

@chatgpt-codex-connector

Copy link
Copy Markdown
Contributor

Codex Review: Didn't find any major issues. Delightful!

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@steipete
steipete force-pushed the fix/openclaw-timeout-propagation branch from 7cb7495 to a9e8aa2 Compare April 24, 2026 03:22
DranboFieldston and others added 6 commits April 24, 2026 04:28
Thread timeoutMs through the dispatcher creation chain so that
per-request (guarded) dispatchers honor the configured LLM timeout
instead of falling back to undici's hardcoded 60s bodyTimeout/headersTimeout.

Changes:
- undici-runtime.ts: createHttp1Agent/ProxyAgent/EnvHttpProxyAgent now accept
  timeoutMs and apply bodyTimeout/headersTimeout to dispatcher options
- ssrf.ts: createPinnedDispatcher accepts timeoutMs and passes it through
- fetch-guard.ts: fetchWithSsrFGuard reads timeout from params or falls back
  to global dispatcher bodyTimeout via getGlobalDispatcher()
- provider-transport-fetch.ts: buildGuardedModelFetch accepts optional
  timeoutMs and passes it to fetchWithSsrFGuard

The global dispatcher timeout (set by ensureGlobalUndiciStreamTimeouts)
is still applied to non-guarded requests. Guarded requests (used by LLM
transports) now also receive the timeout via a fallback to the global
dispatcher when not explicitly provided.

Fixes openclaw#70829
Replace dead-code .options.bodyTimeout read in resolveDispatcherTimeoutMs
with a module-level bridge (_globalUndiciStreamTimeoutMs) set by
ensureGlobalUndiciStreamTimeouts. This avoids reliance on Undici's
non-public .options field and ensures guarded dispatchers inherit the
configured stream timeout instead of falling back to undici's 60s default.

Fixes Greptile P1 and Codex comments on PR openclaw#70831
@steipete
steipete force-pushed the fix/openclaw-timeout-propagation branch from a9e8aa2 to c015e67 Compare April 24, 2026 03:30
@steipete
steipete merged commit 977a4b2 into openclaw:main Apr 24, 2026
65 checks passed
@steipete

Copy link
Copy Markdown
Contributor

Landed via squash onto main.

  • Source head: c015e67
  • Merge commit: 977a4b2
  • Local gate: pnpm test src/config/talk-defaults.test.ts and pnpm check:changed
  • Live proof: OpenClaw guarded-fetch path survived a 65s delayed local response, and isolated-profile Ollama smoke returned ok with ollama/llama3.2:latest.
  • GitHub CI: green on the rebased source head before merge.

Thanks @DranboFieldston!

@vincentkoc vincentkoc added the dedupe:parent Primary canonical item in dedupe cluster label Apr 25, 2026
@vincentkoc vincentkoc self-assigned this Apr 25, 2026
ogt-redknie pushed a commit to ogt-redknie/OPENX that referenced this pull request May 2, 2026
…t) (openclaw#70831)

* fix: propagate timeoutMs to guarded dispatchers

Thread timeoutMs through the dispatcher creation chain so that
per-request (guarded) dispatchers honor the configured LLM timeout
instead of falling back to undici's hardcoded 60s bodyTimeout/headersTimeout.

Changes:
- undici-runtime.ts: createHttp1Agent/ProxyAgent/EnvHttpProxyAgent now accept
  timeoutMs and apply bodyTimeout/headersTimeout to dispatcher options
- ssrf.ts: createPinnedDispatcher accepts timeoutMs and passes it through
- fetch-guard.ts: fetchWithSsrFGuard reads timeout from params or falls back
  to global dispatcher bodyTimeout via getGlobalDispatcher()
- provider-transport-fetch.ts: buildGuardedModelFetch accepts optional
  timeoutMs and passes it to fetchWithSsrFGuard

The global dispatcher timeout (set by ensureGlobalUndiciStreamTimeouts)
is still applied to non-guarded requests. Guarded requests (used by LLM
transports) now also receive the timeout via a fallback to the global
dispatcher when not explicitly provided.

Fixes openclaw#70829

* fix: resolve fallback timeout via module-level bridge variable

Replace dead-code .options.bodyTimeout read in resolveDispatcherTimeoutMs
with a module-level bridge (_globalUndiciStreamTimeoutMs) set by
ensureGlobalUndiciStreamTimeouts. This avoids reliance on Undici's
non-public .options field and ensures guarded dispatchers inherit the
configured stream timeout instead of falling back to undici's 60s default.

Fixes Greptile P1 and Codex comments on PR openclaw#70831

* chore: re-run CI smoke tests

* test: cover guarded dispatcher timeout propagation

* test: align timeout bridge expectation

* docs: note guarded dispatcher timeout fix

---------

Co-authored-by: Peter Steinberger <[email protected]>
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 9, 2026
…t) (openclaw#70831)

* fix: propagate timeoutMs to guarded dispatchers

Thread timeoutMs through the dispatcher creation chain so that
per-request (guarded) dispatchers honor the configured LLM timeout
instead of falling back to undici's hardcoded 60s bodyTimeout/headersTimeout.

Changes:
- undici-runtime.ts: createHttp1Agent/ProxyAgent/EnvHttpProxyAgent now accept
  timeoutMs and apply bodyTimeout/headersTimeout to dispatcher options
- ssrf.ts: createPinnedDispatcher accepts timeoutMs and passes it through
- fetch-guard.ts: fetchWithSsrFGuard reads timeout from params or falls back
  to global dispatcher bodyTimeout via getGlobalDispatcher()
- provider-transport-fetch.ts: buildGuardedModelFetch accepts optional
  timeoutMs and passes it to fetchWithSsrFGuard

The global dispatcher timeout (set by ensureGlobalUndiciStreamTimeouts)
is still applied to non-guarded requests. Guarded requests (used by LLM
transports) now also receive the timeout via a fallback to the global
dispatcher when not explicitly provided.

Fixes openclaw#70829

* fix: resolve fallback timeout via module-level bridge variable

Replace dead-code .options.bodyTimeout read in resolveDispatcherTimeoutMs
with a module-level bridge (_globalUndiciStreamTimeoutMs) set by
ensureGlobalUndiciStreamTimeouts. This avoids reliance on Undici's
non-public .options field and ensures guarded dispatchers inherit the
configured stream timeout instead of falling back to undici's 60s default.

Fixes Greptile P1 and Codex comments on PR openclaw#70831

* chore: re-run CI smoke tests

* test: cover guarded dispatcher timeout propagation

* test: align timeout bridge expectation

* docs: note guarded dispatcher timeout fix

---------

Co-authored-by: Peter Steinberger <[email protected]>
globalcaos pushed a commit to globalcaos/tinkerclaw that referenced this pull request May 13, 2026
…t) (openclaw#70831)

* fix: propagate timeoutMs to guarded dispatchers

Thread timeoutMs through the dispatcher creation chain so that
per-request (guarded) dispatchers honor the configured LLM timeout
instead of falling back to undici's hardcoded 60s bodyTimeout/headersTimeout.

Changes:
- undici-runtime.ts: createHttp1Agent/ProxyAgent/EnvHttpProxyAgent now accept
  timeoutMs and apply bodyTimeout/headersTimeout to dispatcher options
- ssrf.ts: createPinnedDispatcher accepts timeoutMs and passes it through
- fetch-guard.ts: fetchWithSsrFGuard reads timeout from params or falls back
  to global dispatcher bodyTimeout via getGlobalDispatcher()
- provider-transport-fetch.ts: buildGuardedModelFetch accepts optional
  timeoutMs and passes it to fetchWithSsrFGuard

The global dispatcher timeout (set by ensureGlobalUndiciStreamTimeouts)
is still applied to non-guarded requests. Guarded requests (used by LLM
transports) now also receive the timeout via a fallback to the global
dispatcher when not explicitly provided.

Fixes openclaw#70829

* fix: resolve fallback timeout via module-level bridge variable

Replace dead-code .options.bodyTimeout read in resolveDispatcherTimeoutMs
with a module-level bridge (_globalUndiciStreamTimeoutMs) set by
ensureGlobalUndiciStreamTimeouts. This avoids reliance on Undici's
non-public .options field and ensures guarded dispatchers inherit the
configured stream timeout instead of falling back to undici's 60s default.

Fixes Greptile P1 and Codex comments on PR openclaw#70831

* chore: re-run CI smoke tests

* test: cover guarded dispatcher timeout propagation

* test: align timeout bridge expectation

* docs: note guarded dispatcher timeout fix

---------

Co-authored-by: Peter Steinberger <[email protected]>
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 24, 2026
…t) (openclaw#70831)

* fix: propagate timeoutMs to guarded dispatchers

Thread timeoutMs through the dispatcher creation chain so that
per-request (guarded) dispatchers honor the configured LLM timeout
instead of falling back to undici's hardcoded 60s bodyTimeout/headersTimeout.

Changes:
- undici-runtime.ts: createHttp1Agent/ProxyAgent/EnvHttpProxyAgent now accept
  timeoutMs and apply bodyTimeout/headersTimeout to dispatcher options
- ssrf.ts: createPinnedDispatcher accepts timeoutMs and passes it through
- fetch-guard.ts: fetchWithSsrFGuard reads timeout from params or falls back
  to global dispatcher bodyTimeout via getGlobalDispatcher()
- provider-transport-fetch.ts: buildGuardedModelFetch accepts optional
  timeoutMs and passes it to fetchWithSsrFGuard

The global dispatcher timeout (set by ensureGlobalUndiciStreamTimeouts)
is still applied to non-guarded requests. Guarded requests (used by LLM
transports) now also receive the timeout via a fallback to the global
dispatcher when not explicitly provided.

Fixes openclaw#70829

* fix: resolve fallback timeout via module-level bridge variable

Replace dead-code .options.bodyTimeout read in resolveDispatcherTimeoutMs
with a module-level bridge (_globalUndiciStreamTimeoutMs) set by
ensureGlobalUndiciStreamTimeouts. This avoids reliance on Undici's
non-public .options field and ensures guarded dispatchers inherit the
configured stream timeout instead of falling back to undici's 60s default.

Fixes Greptile P1 and Codex comments on PR openclaw#70831

* chore: re-run CI smoke tests

* test: cover guarded dispatcher timeout propagation

* test: align timeout bridge expectation

* docs: note guarded dispatcher timeout fix

---------

Co-authored-by: Peter Steinberger <[email protected]>
jameslcowan pushed a commit to jameslcowan/openclaw that referenced this pull request Jun 2, 2026
…t) (openclaw#70831)

* fix: propagate timeoutMs to guarded dispatchers

Thread timeoutMs through the dispatcher creation chain so that
per-request (guarded) dispatchers honor the configured LLM timeout
instead of falling back to undici's hardcoded 60s bodyTimeout/headersTimeout.

Changes:
- undici-runtime.ts: createHttp1Agent/ProxyAgent/EnvHttpProxyAgent now accept
  timeoutMs and apply bodyTimeout/headersTimeout to dispatcher options
- ssrf.ts: createPinnedDispatcher accepts timeoutMs and passes it through
- fetch-guard.ts: fetchWithSsrFGuard reads timeout from params or falls back
  to global dispatcher bodyTimeout via getGlobalDispatcher()
- provider-transport-fetch.ts: buildGuardedModelFetch accepts optional
  timeoutMs and passes it to fetchWithSsrFGuard

The global dispatcher timeout (set by ensureGlobalUndiciStreamTimeouts)
is still applied to non-guarded requests. Guarded requests (used by LLM
transports) now also receive the timeout via a fallback to the global
dispatcher when not explicitly provided.

Fixes openclaw#70829

* fix: resolve fallback timeout via module-level bridge variable

Replace dead-code .options.bodyTimeout read in resolveDispatcherTimeoutMs
with a module-level bridge (_globalUndiciStreamTimeoutMs) set by
ensureGlobalUndiciStreamTimeouts. This avoids reliance on Undici's
non-public .options field and ensures guarded dispatchers inherit the
configured stream timeout instead of falling back to undici's 60s default.

Fixes Greptile P1 and Codex comments on PR openclaw#70831

* chore: re-run CI smoke tests

* test: cover guarded dispatcher timeout propagation

* test: align timeout bridge expectation

* docs: note guarded dispatcher timeout fix

---------

Co-authored-by: Peter Steinberger <[email protected]>
sablehead pushed a commit to sablehead/openclaw that referenced this pull request Jun 10, 2026
…t) (openclaw#70831)

* fix: propagate timeoutMs to guarded dispatchers

Thread timeoutMs through the dispatcher creation chain so that
per-request (guarded) dispatchers honor the configured LLM timeout
instead of falling back to undici's hardcoded 60s bodyTimeout/headersTimeout.

Changes:
- undici-runtime.ts: createHttp1Agent/ProxyAgent/EnvHttpProxyAgent now accept
  timeoutMs and apply bodyTimeout/headersTimeout to dispatcher options
- ssrf.ts: createPinnedDispatcher accepts timeoutMs and passes it through
- fetch-guard.ts: fetchWithSsrFGuard reads timeout from params or falls back
  to global dispatcher bodyTimeout via getGlobalDispatcher()
- provider-transport-fetch.ts: buildGuardedModelFetch accepts optional
  timeoutMs and passes it to fetchWithSsrFGuard

The global dispatcher timeout (set by ensureGlobalUndiciStreamTimeouts)
is still applied to non-guarded requests. Guarded requests (used by LLM
transports) now also receive the timeout via a fallback to the global
dispatcher when not explicitly provided.

Fixes openclaw#70829

* fix: resolve fallback timeout via module-level bridge variable

Replace dead-code .options.bodyTimeout read in resolveDispatcherTimeoutMs
with a module-level bridge (_globalUndiciStreamTimeoutMs) set by
ensureGlobalUndiciStreamTimeouts. This avoids reliance on Undici's
non-public .options field and ensures guarded dispatchers inherit the
configured stream timeout instead of falling back to undici's 60s default.

Fixes Greptile P1 and Codex comments on PR openclaw#70831

* chore: re-run CI smoke tests

* test: cover guarded dispatcher timeout propagation

* test: align timeout bridge expectation

* docs: note guarded dispatcher timeout fix

---------

Co-authored-by: Peter Steinberger <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling dedupe:parent Primary canonical item in dedupe cluster size: M

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix: propagate timeoutMs to guarded dispatchers (local LLM 60s timeout)

3 participants