{"record":{"id":"75bee054cfddc89f","repo":"D4Vinci/Scrapling","slug":"invalid-hostname-for-the-cdp-url","errorCode":null,"errorMessage":"Invalid hostname for the CDP URL","messagePattern":"Invalid hostname for the CDP URL","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"scrapling/engines/_browsers/_validators.py","lineNumber":112,"sourceCode":"    dns_over_https: bool = False\n\n    def __post_init__(self):  # pragma: no cover\n        \"\"\"Custom validation after msgspec validation\"\"\"\n        if self.page_action and not callable(self.page_action):\n            raise TypeError(f\"page_action must be callable, got {type(self.page_action).__name__}\")\n        if self.page_setup and not callable(self.page_setup):\n            raise TypeError(f\"page_setup must be callable, got {type(self.page_setup).__name__}\")\n        if self.proxy and self.proxy_rotator:\n            raise ValueError(\n                \"Cannot use 'proxy_rotator' together with 'proxy'. \"\n                \"Use either a static proxy or proxy rotation, not both.\"\n            )\n        if self.proxy:\n            self.proxy = construct_proxy_dict(self.proxy)\n        if self.cdp_url:\n            cdp_msg = _is_invalid_cdp_url(self.cdp_url)\n            if cdp_msg:\n                raise ValueError(cdp_msg)\n\n        if not self.cookies:\n            self.cookies = []\n        if not self.extra_flags:\n            self.extra_flags = []\n        if not self.selector_config:\n            self.selector_config = {}\n        if not self.additional_args:\n            self.additional_args = {}\n        if not self.capture_xhr:\n            self.capture_xhr = None\n\n        if self.init_script is not None:\n            validation_msg = _is_invalid_file_path(self.init_script)\n            if validation_msg:\n                raise ValueError(validation_msg)\n\n        if self.executable_path is not None:","sourceCodeStart":94,"sourceCodeEnd":130,"githubUrl":"https://github.com/D4Vinci/Scrapling/blob/5d213a2d4764002bfc4fed33c32fe09fa8b0bf7f/scrapling/engines/_browsers/_validators.py#L94-L130","documentation":"Returned by _is_invalid_cdp_url (and raised as ValueError from __post_init__) when the URL has a valid scheme but urlparse finds an empty network location — i.e. no host. Example: 'ws:///path' has no hostname. Marked pragma: no cover because it is an edge case rarely hit in practice.","triggerScenarios":"Passing a CDP URL like 'wss://' or 'ws:///devtools/browser/id' where the authority component (host[:port]) is missing.","commonSituations":"Programmatically building the CDP URL and dropping the host segment, or string-formatting bugs that leave the netloc empty.","solutions":["Include the host (and usually port): ws://127.0.0.1:9222/devtools/browser/<id>","Double-check URL construction code for empty host placeholders"],"exampleFix":"// before\nsession = StealthySession(cdp_url='ws:///devtools/browser/abc')\n\n// after\nsession = StealthySession(cdp_url='ws://127.0.0.1:9222/devtools/browser/abc')","handlingStrategy":"validation","validationCode":"from urllib.parse import urlparse\np = urlparse(cdp_url)\nassert p.netloc, 'cdp_url must include a host'","typeGuard":"def cdp_url_has_host(u: str) -> bool:\n    from urllib.parse import urlparse\n    return bool(urlparse(u).netloc)","tryCatchPattern":null,"preventionTips":["Build CDP URLs from host + port variables so the netloc can never be empty","Smoke-test constructed URLs with urlparse before session creation"],"tags":["validation","cdp","url","edge-case"],"backgroundTag":null,"analyzedSha":"5d213a2d4764002bfc4fed33c32fe09fa8b0bf7f","analyzedAt":"2026-08-14T22:23:09.440Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}