#23075: fix(browser): merge top-level ref/targetId into act request body
agents
size: XS
Cluster:
Error Handling Improvements
## Summary
- Fixes the browser tool `act` action failing with `"ref is required"` when the LLM passes `ref` as a top-level tool parameter instead of nesting it inside the `request` object
- The `BrowserToolSchema` exposes `ref` at both the top level and inside `BrowserActSchema`, so LLMs (particularly Gemini via LiteLLM) commonly place it at the top level
- The handler now spreads `params.request` and merges in `params.ref` and `params.targetId` when present, matching what the CLI commands (click, type, hover) already do
## Root cause
`BrowserToolSchema` defines `ref` as an optional top-level field (line 102) and `BrowserActSchema` also defines `ref` inside the `request` object (line 52). When an LLM places `ref` at the top level, the `case "act"` handler forwarded only `params.request` to the `/act` endpoint — which didn't contain `ref`. The backend validation then rejected the request.
This made **all** browser interactions (click, type, hover) from the agent fail consistently, even on trivial static pages like example.com.
## Test plan
- [x] Verified fix works locally with OpenClaw agent — snapshot then click succeeds
- [x] `pnpm check` passes (formatting, type checking, linting)
- [x] `pnpm test` — 854 files pass, 6742 tests pass (7 pre-existing symlink failures on Windows unrelated to this change)
- [x] CLI browser commands continue to work unchanged
- [x] When `ref` is already inside `request`, the top-level value is not used (no override)
## AI disclosure
This PR was AI-assisted (Claude Opus 4.6). The bug was diagnosed by tracing the code path from the agent tool handler through to the backend `/act` endpoint validation. The fix was tested on a live OpenClaw instance before submitting.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
This PR fixes the browser tool `act` action failing with `"ref is required"` when LLMs (e.g., Gemini via LiteLLM) place `ref` and `targetId` as top-level tool parameters instead of nesting them inside the `request` object. The fix spreads `params.request` into a mutable copy and merges in top-level `ref`/`targetId` when present (without overriding values already in `request`).
- **Logic bug**: The spread-then-validate pattern (`{ ...(params.request as ...) }`) breaks the `"request required"` guard. `{ ...undefined }` produces `{}` in JavaScript, which is truthy and passes `typeof === "object"`, so the validation can never throw when `params.request` is missing. The validation should happen *before* spreading into a new object.
- The merge logic itself (lines 790-795) is correct: it only fills in `ref`/`targetId` when the request object doesn't already contain them, preventing unintentional overrides.
<h3>Confidence Score: 3/5</h3>
- The core idea is sound but the implementation has a validation bypass bug that should be fixed before merging.
- The PR correctly identifies a real issue (LLMs placing ref/targetId at the top level) and the merge logic is correct, but the refactoring of the spread operator breaks the existing "request required" validation — { ...undefined } returns {} which is truthy, so missing request objects will no longer be caught early.
- `src/agents/tools/browser-tool.ts` — the spread-then-validate pattern on lines 781-783 needs to be reordered to validate first, then spread.
<sub>Last reviewed commit: b2edbcf</sub>
<!-- greptile_other_comments_section -->
<sub>(2/5) Greptile learns from your feedback when you react with thumbs up/down!</sub>
<!-- /greptile_comment -->
Most Similar PRs
#18888: Fix: Merge targetId into request body for browser act operations
by jriff · 2026-02-17
80.7%
#14728: fix(browser): add targetUrl description and improve error messages ...
by lailoo · 2026-02-12
78.1%
#23668: fix: distinguish browser validation errors from connectivity failures
by davidemanuelDEV · 2026-02-22
75.4%
#18907: Fix: Improve browser error messages to avoid misleading agents
by jriff · 2026-02-17
74.8%
#12378: feat: expose download and waitForDownload actions in browser agent ...
by vabole · 2026-02-09
73.9%
#15071: fix(browser): default fill field type to textbox when omitted
by Ayush10 · 2026-02-12
73.1%
#3794: fix(browser-tool): disallow close without targetId to avoid unsafe ...
by JaydenLiang · 2026-01-29
72.8%
#9693: fix: add configurable browser.actTimeoutMs to prevent action timeouts
by JitendraZaa · 2026-02-05
72.0%
#22952: fix(browser): suggest remote profile when local Chrome is not insta...
by dashed · 2026-02-21
71.8%
#14328: fix: strip incomplete tool_use blocks from errored/aborted messages...
by Kropiunig · 2026-02-12
71.7%