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
- Pass only browser_session when working from a browser session: await extract_markdown(browser_session=session)
- Pass only the pair when working at actor level: await extract_markdown(dom_service=svc, target_id=target_id)
- 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
- Choose one input mode per call site and never mix kwargs
- In generic wrappers, branch on which argument is non-None before forwarding
- Encode the either/or contract in your function signatures with overloads or Union input types
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
- Must provide either browser_session or both dom_service and
- Cannot specify both "browser" and "browser_session" paramete
- Cannot specify both "skills" and "skill_ids" parameters. Use
- Cannot provide both file_system_state (from agent state) and
- Set trigger_char_count or trigger_token_count, not both.
AI-assisted analysis of browser-use/browser-use@6c73fced2f (2026-08-14).
Data as JSON: /api/errors/af8f72aae8e00b22.
Report an issue: GitHub.