D4Vinci/Scrapling · error · ValueError

Browser executable path not found: {value}

Error message

Browser executable path not found: {value}

What it means

Raised when `executable_path` (a custom Chromium/Chrome binary) does not exist on disk. _is_invalid_file_path is called with label 'Browser executable' and its first check Path(value).exists() fails, so the ValueError reports the browser executable path as not found.

Source

Thrown at scrapling/engines/_browsers/_validators.py:133

            self.cookies = []
        if not self.extra_flags:
            self.extra_flags = []
        if not self.selector_config:
            self.selector_config = {}
        if not self.additional_args:
            self.additional_args = {}
        if not self.capture_xhr:
            self.capture_xhr = None

        if self.init_script is not None:
            validation_msg = _is_invalid_file_path(self.init_script)
            if validation_msg:
                raise ValueError(validation_msg)

        if self.executable_path is not None:
            validation_msg = _is_invalid_file_path(self.executable_path, "Browser executable")
            if validation_msg:
                raise ValueError(validation_msg)

        if self.block_ads:
            from scrapling.engines.toolbelt.ad_domains import AD_DOMAINS

            if self.blocked_domains:
                self.blocked_domains = self.blocked_domains | set(AD_DOMAINS)
            else:
                self.blocked_domains = set(AD_DOMAINS)


class StealthConfig(PlaywrightConfig, kw_only=True, frozen=False, weakref=True):
    allow_webgl: bool = True
    hide_canvas: bool = False
    block_webrtc: bool = False
    solve_cloudflare: bool = False

    def __post_init__(self):
        """Custom validation after msgspec validation"""

View on GitHub (pinned to 5d213a2d47)

Solutions

  1. Verify the binary location: `which google-chrome` / `which chromium` and use that absolute path
  2. Or omit executable_path and let camoufox/playwright manage the browser
  3. In Docker, install the browser or copy it to the expected path

Example fix

// before
session = StealthySession(executable_path='/usr/bin/chromium')  # not installed

// after
import shutil
chrome = shutil.which('google-chrome') or shutil.which('chromium')
session = StealthySession(executable_path=chrome) if chrome else StealthySession()
Defensive patterns

Strategy: validation

Validate before calling

from pathlib import Path
if executable_path and not Path(executable_path).exists():
    executable_path = shutil.which('google-chrome') or shutil.which('chromium')
    assert executable_path, 'no system chrome found; install one or drop executable_path'

Type guard

def browser_binary_exists(p: str | None) -> bool:
    return p is None or Path(p).exists()

Try / catch

try:
    session = StealthySession(executable_path=p)
except ValueError as e:
    if 'not found' in str(e):
        import shutil
        session = StealthySession(executable_path=shutil.which('chromium'))
    else:
        raise

Prevention

When it happens

Trigger: Passing executable_path='/usr/bin/google-chrome' on a machine where it is not installed, or after the browser was upgraded/moved; wrong path in Docker images.

Common situations: Shipping configs between environments (macOS dev -> Linux container), depending on a system Chrome that is not installed, or Playwright's own browsers not downloaded and a wrong fallback path given.

Related errors


AI-assisted analysis of D4Vinci/Scrapling@5d213a2d47 (2026-08-14). Data as JSON: /api/errors/b304deac213038ab. Report an issue: GitHub.