{"record":{"id":"af8f72aae8e00b22","repo":"browser-use/browser-use","slug":"cannot-specify-both-browser-session-and-dom-servic","errorCode":null,"errorMessage":"Cannot specify both browser_session and dom_service/target_id","messagePattern":"Cannot specify both browser_session and dom_service/target_id","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"browser_use/dom/markdown_extractor.py","lineNumber":51,"sourceCode":"\tor a DOM service with target ID (for page actor).\n\n\tArgs:\n\t    browser_session: Browser session to extract content from (tools service path)\n\t    dom_service: DOM service instance (page actor path)\n\t    target_id: Target ID for the page (required when using dom_service)\n\t    extract_links: Whether to preserve links in markdown\n\t    extract_images: Whether to preserve inline image src URLs in markdown\n\n\tReturns:\n\t    tuple: (clean_markdown_content, content_statistics)\n\n\tRaises:\n\t    ValueError: If neither browser_session nor (dom_service + target_id) are provided\n\t\"\"\"\n\t# Validate input parameters\n\tif browser_session is not None:\n\t\tif dom_service is not None or target_id is not None:\n\t\t\traise ValueError('Cannot specify both browser_session and dom_service/target_id')\n\t\t# Browser session path (tools service)\n\t\tenhanced_dom_tree = await _get_enhanced_dom_tree_from_browser_session(browser_session)\n\t\tcurrent_url = await browser_session.get_current_page_url()\n\t\tmethod = 'enhanced_dom_tree'\n\telif dom_service is not None and target_id is not None:\n\t\t# DOM service path (page actor)\n\t\t# Lazy fetch all_frames inside get_dom_tree if needed (for cross-origin iframes)\n\t\tenhanced_dom_tree, _ = await dom_service.get_dom_tree(target_id=target_id, all_frames=None)\n\t\tcurrent_url = None  # Not available via DOM service\n\t\tmethod = 'dom_service'\n\telse:\n\t\traise ValueError('Must provide either browser_session or both dom_service and target_id')\n\n\t# Use the HTML serializer with the enhanced DOM tree\n\thtml_serializer = HTMLSerializer(extract_links=extract_links)\n\tpage_html = html_serializer.serialize(enhanced_dom_tree)\n\n\toriginal_html_length = len(page_html)","sourceCodeStart":33,"sourceCodeEnd":69,"githubUrl":"https://github.com/browser-use/browser-use/blob/6c73fced2f6d45a11d88622fe56365a5fe18f28b/browser_use/dom/markdown_extractor.py#L33-L69","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"# before\nmd, stats = await markdown_extractor.extract_markdown(\n    browser_session=session, dom_service=svc, target_id=tid)\n\n# after\nmd, stats = await markdown_extractor.extract_markdown(browser_session=session)","handlingStrategy":"validation","validationCode":"def validate_extractor_args(browser_session=None, dom_service=None, target_id=None):\n    if browser_session is not None:\n        assert dom_service is None and target_id is None, 'pass browser_session alone'\n    else:\n        assert dom_service is not None and target_id is not None, 'pass dom_service AND target_id'","typeGuard":"def has_complete_actor_path(dom_service, target_id) -> bool:\n    return dom_service is not None and target_id is not None","tryCatchPattern":"try:\n    md = await markdown_extractor.extract_markdown(**kwargs)\nexcept ValueError as e:\n    if 'Cannot specify both' in str(e):\n        kwargs.pop('dom_service', None); kwargs.pop('target_id', None)  # or fix at call site\n        md = await markdown_extractor.extract_markdown(**kwargs)\n    else:\n        raise","preventionTips":["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"],"tags":["dom","markdown","validation","api-misuse"],"backgroundTag":null,"analyzedSha":"6c73fced2f6d45a11d88622fe56365a5fe18f28b","analyzedAt":"2026-08-14T19:42:40.557Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}