openclaw - 💡(How to fix) Fix [tools] browser: add subagent-side timeout so lane is not held when browser hangs

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

The subagent side of the [tools] browser call should enforce a short timeout (proposed: 5s default, configurable via tools.browser.callTimeoutMs) and return a recoverable error to the caller within ~5s if the browser tool does not respond. The lane is released, the agent receives the error, and the turn can continue or cleanly fail. 4. Long-running browser operations (page navigation with waitForNetworkIdle, waitForSelector) should still work because they are executed server-side; the subagent-side timeout is only on the HTTP call to the browser server, not on the Playwright action itself. If an action genuinely takes longer than 5s, the server returns its own longer-timeout error; the subagent call completes normally. This fuse is for the case where the server is unreachable or has crashed.

Root Cause

  1. In the subagent runtime (agents/tools/browser.ts or equivalent), wrap the fetch to the browser server in a Promise.race([call, timeout(callTimeoutMs)]).
  2. On timeout: emit an ExecutionError with code browser_call_timeout, release the lane, continue the agent turn.
  3. Default callTimeoutMs: 5000. Expose as tools.browser.callTimeoutMs in openclaw.json schema.
  4. Long-running browser operations (page navigation with waitForNetworkIdle, waitForSelector) should still work because they are executed server-side; the subagent-side timeout is only on the HTTP call to the browser server, not on the Playwright action itself. If an action genuinely takes longer than 5s, the server returns its own longer-timeout error; the subagent call completes normally. This fuse is for the case where the server is unreachable or has crashed.

Fix Action

Fix / Workaround

Workaround

Code Example

# 1. Kill the browser server mid-flight
kill $(lsof -ti :18791)

# 2. Issue a browser tool call from an agent turn
#    (e.g., via agent-turn with a prompt that triggers browser.navigate)

# 3. Observe lane held for >30s waiting for a server that will never respond
#    until the outer gateway request timeout (default ~120s) kicks in
RAW_BUFFERClick to expand / collapse

Version: v2026.4.19-beta.1

Symptom

When [tools] browser hangs (network, page crash, Playwright internal stall), the subagent that called it keeps the execution lane claimed indefinitely. On a single-lane agent workspace, this blocks all subsequent turns for the duration of the hang. Observed lane-wait spikes of 30s to 3min during the 2026-04-17/18 soak window.

Expected

The subagent side of the [tools] browser call should enforce a short timeout (proposed: 5s default, configurable via tools.browser.callTimeoutMs) and return a recoverable error to the caller within ~5s if the browser tool does not respond. The lane is released, the agent receives the error, and the turn can continue or cleanly fail.

Current behavior

The browser tool bridges to the Playwright server at http://127.0.0.1:18791/. If the server itself hangs (not just the requested action), there is no subagent-side fuse. The tool call waits until either the browser server returns or the subagent process is killed externally.

Proposed fix

  1. In the subagent runtime (agents/tools/browser.ts or equivalent), wrap the fetch to the browser server in a Promise.race([call, timeout(callTimeoutMs)]).
  2. On timeout: emit an ExecutionError with code browser_call_timeout, release the lane, continue the agent turn.
  3. Default callTimeoutMs: 5000. Expose as tools.browser.callTimeoutMs in openclaw.json schema.
  4. Long-running browser operations (page navigation with waitForNetworkIdle, waitForSelector) should still work because they are executed server-side; the subagent-side timeout is only on the HTTP call to the browser server, not on the Playwright action itself. If an action genuinely takes longer than 5s, the server returns its own longer-timeout error; the subagent call completes normally. This fuse is for the case where the server is unreachable or has crashed.

Reproduction

# 1. Kill the browser server mid-flight
kill $(lsof -ti :18791)

# 2. Issue a browser tool call from an agent turn
#    (e.g., via agent-turn with a prompt that triggers browser.navigate)

# 3. Observe lane held for >30s waiting for a server that will never respond
#    until the outer gateway request timeout (default ~120s) kicks in

Acceptance

48h sample after fix shows 0 lane-wait spikes ≥7s attributable to [tools] browser. Equivalent configuration key exists in openclaw.json schema docs.

Workaround

None short of killing and restarting the gateway when browser server hangs are detected externally.

extent analysis

TL;DR

Implement a timeout mechanism in the subagent runtime to handle hung browser server calls, releasing the execution lane and returning a recoverable error.

Guidance

  • Wrap the fetch to the browser server in a Promise.race with a timeout, as proposed in the fix plan, to enforce a short timeout (default 5s) and return an ExecutionError on timeout.
  • Expose the callTimeoutMs configuration option in openclaw.json schema to allow for customization of the timeout value.
  • Verify that long-running browser operations are not affected by the subagent-side timeout, as they are executed server-side and should return their own longer-timeout errors if necessary.
  • Test the fix using the provided reproduction steps to ensure the lane is released and the agent turn continues or cleanly fails within the expected timeframe.

Example

// agents/tools/browser.ts
const callTimeoutMs = 5000; // default timeout value
const browserServerUrl = 'http://127.0.0.1:18791/';

const fetchBrowserServer = async () => {
  const call = fetch(browserServerUrl);
  const timeout = new Promise((_, reject) => setTimeout(() => reject(new Error('browser_call_timeout')), callTimeoutMs));
  const result = await Promise.race([call, timeout]);
  // handle result or error
};

Notes

The proposed fix assumes that the browser server's longer-timeout errors will be handled correctly by the subagent. Additional testing may be necessary to ensure this is the case.

Recommendation

Apply the proposed workaround by implementing the Promise.race timeout mechanism in the subagent runtime, as it provides a clear and effective solution to the problem of hung browser server calls blocking the execution lane.

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 [tools] browser: add subagent-side timeout so lane is not held when browser hangs