browser-use/browser-use · error · ValueError

JavaScript code must start with (...args) => format. Got: {p

Error message

JavaScript code must start with (...args) => format. Got: {page_function[:50]}...

What it means

Raised by ActorPage.evaluate() when the page_function does not begin with '(' or does not contain '=>'. The library only accepts JavaScript written as an arrow function, e.g. '() => document.title', because it wraps the code as '({page_function})()' before sending it to CDP Runtime.evaluate. Any other syntax (bare statements, 'function' declarations, async IIFE) is rejected before execution.

Source

Thrown at browser_use/actor/page.py:121

	async def evaluate(self, page_function: str, *args) -> str:
		"""Execute JavaScript in the target.

		Args:
			page_function: JavaScript code that MUST start with (...args) => format
			*args: Arguments to pass to the function

		Returns:
			String representation of the JavaScript execution result.
			Objects and arrays are JSON-stringified.
		"""
		session_id = await self._ensure_session()

		# Clean and fix common JavaScript string parsing issues
		page_function = self._fix_javascript_string(page_function)

		# Enforce arrow function format
		if not (page_function.startswith('(') and '=>' in page_function):
			raise ValueError(f'JavaScript code must start with (...args) => format. Got: {page_function[:50]}...')

		# Build the expression - call the arrow function with provided args
		if args:
			# Convert args to JSON representation for safe passing
			import json

			arg_strs = [json.dumps(arg) for arg in args]
			expression = f'({page_function})({", ".join(arg_strs)})'
		else:
			expression = f'({page_function})()'

		# Debug: log the actual expression being evaluated
		logger.debug(f'Evaluating JavaScript: {repr(expression)}')

		params: 'EvaluateParameters' = {'expression': expression, 'returnByValue': True, 'awaitPromise': True}
		result = await self._client.send.Runtime.evaluate(
			params,
			session_id=session_id,

View on GitHub (pinned to 6c73fced2f)

Solutions

  1. Rewrite the code as an arrow function: page.evaluate('() => document.title')
  2. Wrap statements in a zero-arg arrow body: page.evaluate('() => { window.scrollTo(0, 500); return "done"; }')
  3. If the string came from an LLM tool call, pre-validate it client-side with a regex like r'^\\s*\\(.*=>'
  4. Upgrade/check version: newer browser-use releases may auto-wrap non-arrow code in some code paths, but do not rely on it

Example fix

# before
await page.evaluate('document.title')

# after
await page.evaluate('() => document.title')
Defensive patterns

Strategy: validation

Validate before calling

import re
ARROW_RE = re.compile(r'^\s*\(.*=>', re.DOTALL)
def as_arrow_fn(js: str) -> str:
    js = js.strip()
    if not ARROW_RE.match(js):
        return f'() => {{ {js} }}' if not js.endswith(';') else f'() => {{ {js} }}'
    return js

page_function = as_arrow_fn(page_function)
assert page_function.startswith('(') and '=>' in page_function

Type guard

def is_arrow_function(code: str) -> bool:
    code = code.strip()
    return code.startswith('(') and '=>' in code

Try / catch

try:
    await page.evaluate(js)
except ValueError as e:
    if 'must start with' in str(e):
        await page.evaluate(f'() => {{ {js} }}')  # one-shot repair
    else:
        raise

Prevention

When it happens

Trigger: Calling page.evaluate('document.title') or page.evaluate('return 1') (no arrow syntax); passing a statement string like 'window.scrollTo(0, 500)'; passing code that starts with whitespace/quote wrappers that _fix_javascript_string strips so the leading '(' is lost.

Common situations: Developers coming from Playwright's page.evaluate which accepts expressions and function bodies; LLM-generated evaluate actions that emit plain JS statements instead of arrow functions; double-quoted strings that get unwrapped and mangled by the cleaning step.

Related errors


AI-assisted analysis of browser-use/browser-use@6c73fced2f (2026-08-14). Data as JSON: /api/errors/0572250b84679985. Report an issue: GitHub.