D4Vinci/Scrapling · error · ValueError

Browser executable is not a file: {value}

Error message

Browser executable is not a file: {value}

What it means

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.

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. Point to the binary file itself, e.g. .../chrome-linux/chrome
  2. Use shutil.which() to get the exact executable file path

Example fix

// before
session = StealthySession(executable_path='~/.cache/ms-playwright/chromium-1234/chrome-linux')

// after
session = StealthySession(executable_path=str(Path.home()/'.cache/ms-playwright/chromium-1234/chrome-linux/chrome'))
Defensive patterns

Strategy: validation

Validate before calling

from pathlib import Path
assert Path(executable_path).is_file(), 'point at the browser binary, not its folder'

Type guard

def is_browser_binary(p: str) -> bool:
    path = Path(p)
    return path.is_file() and path.stat().st_size > 1_000_000  # binaries are large

Prevention

When it happens

Trigger: 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.

Common situations: Discovering the playwright cache layout and stopping one level short of the actual binary.

Related errors


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