{"record":{"id":"8647167a0e6f1ca8","repo":"D4Vinci/Scrapling","slug":"browser-executable-is-not-a-file-value","errorCode":null,"errorMessage":"Browser executable is not a file: {value}","messagePattern":"Browser executable is not a file: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"scrapling/engines/_browsers/_validators.py","lineNumber":133,"sourceCode":"            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:\n            validation_msg = _is_invalid_file_path(self.executable_path, \"Browser executable\")\n            if validation_msg:\n                raise ValueError(validation_msg)\n\n        if self.block_ads:\n            from scrapling.engines.toolbelt.ad_domains import AD_DOMAINS\n\n            if self.blocked_domains:\n                self.blocked_domains = self.blocked_domains | set(AD_DOMAINS)\n            else:\n                self.blocked_domains = set(AD_DOMAINS)\n\n\nclass StealthConfig(PlaywrightConfig, kw_only=True, frozen=False, weakref=True):\n    allow_webgl: bool = True\n    hide_canvas: bool = False\n    block_webrtc: bool = False\n    solve_cloudflare: bool = False\n\n    def __post_init__(self):\n        \"\"\"Custom validation after msgspec validation\"\"\"","sourceCodeStart":115,"sourceCodeEnd":151,"githubUrl":"https://github.com/D4Vinci/Scrapling/blob/5d213a2d4764002bfc4fed33c32fe09fa8b0bf7f/scrapling/engines/_browsers/_validators.py#L115-L151","documentation":"Raised when executable_path exists but is not a regular file — usually a directory (e.g. pointing at /usr/bin or the playwright browsers folder instead of the binary inside it). Playwright needs the executable file itself.","triggerScenarios":"Passing the browsers directory (e.g. ~/.cache/ms-playwright/chromium-1234/chrome-linux) rather than the chrome binary inside it, or a directory in PATH.","commonSituations":"Discovering the playwright cache layout and stopping one level short of the actual binary.","solutions":["Point to the binary file itself, e.g. .../chrome-linux/chrome","Use shutil.which() to get the exact executable file path"],"exampleFix":"// before\nsession = StealthySession(executable_path='~/.cache/ms-playwright/chromium-1234/chrome-linux')\n\n// after\nsession = StealthySession(executable_path=str(Path.home()/'.cache/ms-playwright/chromium-1234/chrome-linux/chrome'))","handlingStrategy":"validation","validationCode":"from pathlib import Path\nassert Path(executable_path).is_file(), 'point at the browser binary, not its folder'","typeGuard":"def is_browser_binary(p: str) -> bool:\n    path = Path(p)\n    return path.is_file() and path.stat().st_size > 1_000_000  # binaries are large","tryCatchPattern":null,"preventionTips":["Walk one level deeper in playwright cache dirs: the binary is chrome-linux/chrome, not chrome-linux/","Use shutil.which() to obtain exact file paths"],"tags":["validation","file-path","browser-binary"],"backgroundTag":null,"analyzedSha":"5d213a2d4764002bfc4fed33c32fe09fa8b0bf7f","analyzedAt":"2026-08-14T22:23:09.440Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}