← Back to PRs

#9693: fix: add configurable browser.actTimeoutMs to prevent action timeouts

by JitendraZaa open 2026-02-05 15:39 View on GitHub →
docs agents stale size: M
## Problem All browser HTTP API calls (click, type, navigate, snapshot, cookies, storage, etc.) use a **hardcoded 20-second HTTP timeout**. However, many real-world browser operations — especially multi-step CDP interactions like click, type, hover, drag, and fill — involve 4 or more sequential DOM queries that regularly exceed 20 seconds on slow-loading or complex pages. This causes the following error even when the underlying Playwright operation is still running successfully: ``` Error: Can't reach the OpenClaw browser control service (timed out after 20000ms) ``` The root cause is a **mismatch between two timeout layers**: | Layer | Default | What it controls | |-------|---------|-----------------| | HTTP fetch timeout (client to service) | 20,000ms (hardcoded) | How long the HTTP request waits for a response | | Playwright action timeout (service to DOM) | 8,000ms (per-request, overridable) | How long Playwright waits for the DOM action | Even when a caller sets a longer Playwright-level timeout via `req.timeoutMs`, the HTTP fetch still aborts at 20 seconds — making the Playwright timeout effectively useless for slow pages. ## Solution This PR adds a new user-configurable option `browser.actTimeoutMs` that controls the HTTP-level timeout for all browser action requests. It follows the same pattern already established by `remoteCdpTimeoutMs` in the codebase. ### What changed - **Default timeout increased from 20s to 30s** — a backward-compatible improvement that immediately reduces timeout failures on moderately slow pages without requiring any user configuration - **New config option `browser.actTimeoutMs`** — users experiencing timeouts on slow pages can now increase this value - **Smart auto-extension for `browserAct`** — the HTTP timeout automatically extends to `max(actTimeoutMs, req.timeoutMs + 5000)` so the HTTP layer never kills a request while Playwright is still working on it ### How to configure Via config file: ```yaml browser: actTimeoutMs: 60000 # 60 seconds for click/type operations ``` Or via CLI: ``` openclaw config set browser.actTimeoutMs 60000 ``` ### After this change | Layer | Before | After | |-------|--------|-------| | HTTP fetch timeout | 20,000ms (hardcoded, not configurable) | **30,000ms** default, user-configurable via `browser.actTimeoutMs` | | Playwright action timeout | 8,000ms (per-request) | Unchanged | ## Files changed - **`src/browser/constants.ts`** — Added `DEFAULT_BROWSER_ACT_TIMEOUT_MS = 30_000` constant - **`src/config/types.browser.ts`** — Added `actTimeoutMs?: number` to the `BrowserConfig` type - **`src/browser/config.ts`** — Resolves and validates `actTimeoutMs` (handles negative values, NaN, and undefined gracefully) - **`src/browser/client-actions-core.ts`** — Updated all 7 functions (`browserNavigate`, `browserArmDialog`, `browserArmFileChooser`, `browserWaitForDownload`, `browserDownload`, `browserAct`, `browserScreenshotAction`) to use configurable timeout - **`src/browser/client-actions-observe.ts`** — Updated all 8 observation functions to use configurable timeout - **`src/browser/client-actions-state.ts`** — Updated all 15 state management functions to use configurable timeout - **`src/browser/client.ts`** — Updated 3 functions (`browserResetProfile`, `browserDeleteProfile`, `browserSnapshot`) to use configurable timeout - **`src/agents/tools/browser-tool.ts`** — Loads `actTimeoutMs` from resolved config and passes it through to all client calls - **`src/agents/sandbox/browser.ts`** — Added `actTimeoutMs` to sandbox browser config construction - **`docs/tools/browser.md`** — Documented the new config option with usage examples ## Testing - All 5,410 existing tests pass - Added new tests for `actTimeoutMs` config resolution (default value, custom value, and fallback for invalid values like negative numbers and NaN) - Updated existing test expectations to reflect the new 30s default - Build and lint pass cleanly ## Related issues Addresses #6472, #5361, #7665, #2929 <!-- greptile_comment --> <h3>Greptile Summary</h3> This PR adds a new user-configurable `browser.actTimeoutMs` option to control the HTTP-level timeout for browser action requests, replacing the previous hardcoded 20s timeout with a 30s default. The change spans the full stack: Zod config schema, type definition, config resolution (supporting both numbers and duration strings like `"30s"`), and all ~30 browser client functions across four files. The `browserAct` function additionally gets a smart auto-extension (`Math.max(actTimeoutMs, req.timeoutMs + 5000)`) to ensure the HTTP timeout always exceeds the Playwright-level action timeout. - **Config resolution** is well-implemented with proper validation for negative, NaN, zero, and invalid string values, plus duration string support via `parseDurationMs`. - **`browserAct` auto-extension** correctly addresses the core timeout mismatch described in the PR, but several other functions (`browserArmDialog`, `browserArmFileChooser`, `browserWaitForDownload`, `browserDownload`, `browserResponseBody`) accept a Playwright-level `timeoutMs` without the same auto-extension — leaving the original mismatch possible for those code paths. - Tests cover config resolution (number, duration string, invalid inputs) and updated expectations for the new 30s default. <h3>Confidence Score: 3/5</h3> - This PR is safe to merge but has an incomplete fix — the smart auto-extension logic only covers `browserAct`, leaving other timeout-sensitive functions vulnerable to the same mismatch. - The core config infrastructure (constant, type, schema, resolver, tests) is solid. The auto-extension logic in `browserAct` correctly solves the described problem. However, several other functions (`browserArmDialog`, `browserArmFileChooser`, `browserWaitForDownload`, `browserDownload`, `browserResponseBody`) accept a Playwright-level `timeoutMs` but don't apply the same auto-extension, meaning the HTTP timeout can still kill requests while the server is still processing. The 30s default (up from 20s) partially mitigates this, but the root cause remains for those code paths when users set longer Playwright timeouts. - Pay close attention to `src/browser/client-actions-core.ts` — functions `browserArmDialog`, `browserArmFileChooser`, `browserWaitForDownload`, and `browserDownload` need the same auto-extension logic as `browserAct`. <sub>Last reviewed commit: 2d39b20</sub> <!-- greptile_other_comments_section --> <!-- /greptile_comment -->

Most Similar PRs