browser-use/browser-use · error · ValueError

Cannot specify both browser_session and dom_service/target_i

Error message

Cannot specify both browser_session and dom_service/target_id

What it means

Raised by the markdown extractor's input validation: extract_markdown-style entry points accept EITHER a browser_session (tools-service path, which fetches the enhanced DOM tree itself) OR a dom_service plus target_id (page-actor path). Passing both at once is ambiguous — the function would not know which DOM source to serialize — so it rejects the combination immediately.

Source

Thrown at browser_use/dom/markdown_extractor.py:51

	or a DOM service with target ID (for page actor).

	Args:
	    browser_session: Browser session to extract content from (tools service path)
	    dom_service: DOM service instance (page actor path)
	    target_id: Target ID for the page (required when using dom_service)
	    extract_links: Whether to preserve links in markdown
	    extract_images: Whether to preserve inline image src URLs in markdown

	Returns:
	    tuple: (clean_markdown_content, content_statistics)

	Raises:
	    ValueError: If neither browser_session nor (dom_service + target_id) are provided
	"""
	# Validate input parameters
	if browser_session is not None:
		if dom_service is not None or target_id is not None:
			raise ValueError('Cannot specify both browser_session and dom_service/target_id')
		# Browser session path (tools service)
		enhanced_dom_tree = await _get_enhanced_dom_tree_from_browser_session(browser_session)
		current_url = await browser_session.get_current_page_url()
		method = 'enhanced_dom_tree'
	elif dom_service is not None and target_id is not None:
		# DOM service path (page actor)
		# Lazy fetch all_frames inside get_dom_tree if needed (for cross-origin iframes)
		enhanced_dom_tree, _ = await dom_service.get_dom_tree(target_id=target_id, all_frames=None)
		current_url = None  # Not available via DOM service
		method = 'dom_service'
	else:
		raise ValueError('Must provide either browser_session or both dom_service and target_id')

	# Use the HTML serializer with the enhanced DOM tree
	html_serializer = HTMLSerializer(extract_links=extract_links)
	page_html = html_serializer.serialize(enhanced_dom_tree)

	original_html_length = len(page_html)

View on GitHub (pinned to 6c73fced2f)

Solutions

  1. Pass only browser_session when working from a browser session: await extract_markdown(browser_session=session)
  2. Pass only the pair when working at actor level: await extract_markdown(dom_service=svc, target_id=target_id)
  3. In wrapper functions, branch explicitly on which input the caller supplied before forwarding

Example fix

# before
md, stats = await markdown_extractor.extract_markdown(
    browser_session=session, dom_service=svc, target_id=tid)

# after
md, stats = await markdown_extractor.extract_markdown(browser_session=session)
Defensive patterns

Strategy: validation

Validate before calling

def validate_extractor_args(browser_session=None, dom_service=None, target_id=None):
    if browser_session is not None:
        assert dom_service is None and target_id is None, 'pass browser_session alone'
    else:
        assert dom_service is not None and target_id is not None, 'pass dom_service AND target_id'

Type guard

def has_complete_actor_path(dom_service, target_id) -> bool:
    return dom_service is not None and target_id is not None

Try / catch

try:
    md = await markdown_extractor.extract_markdown(**kwargs)
except ValueError as e:
    if 'Cannot specify both' in str(e):
        kwargs.pop('dom_service', None); kwargs.pop('target_id', None)  # or fix at call site
        md = await markdown_extractor.extract_markdown(**kwargs)
    else:
        raise

Prevention

When it happens

Trigger: Calling the extractor with extract_markdown(browser_session=session, dom_service=svc, target_id='ABC') or any mix of browser_session with dom_service/target_id in kwargs.

Common situations: Refactoring call sites from the browser-session API to the DOM-service API and leaving the old argument behind; IDE autocompletion filling all optional params; wrapper functions that blindly forward **kwargs from two different callers.

Related errors


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