How to Specify a Custom Browser Executable Path in Zendriver

Pass the browser_executable_path parameter to zd.start(), Config(), or Browser.create() to override Zendriver's auto-discovery mechanism and point to any Chromium-based binary.

Zendriver is a Python library for controlling Chromium-based browsers through the Chrome DevTools Protocol. When you need to run a portable browser build, a custom-compiled version, or an installation located outside standard system directories, you must specify a custom browser executable path to ensure Zendriver launches the correct binary.

Why Specify a Custom Browser Executable Path?

By default, Zendriver automatically discovers browser binaries by scanning common installation directories and the system PATH. However, auto-discovery fails in several scenarios:

  • Portable builds stored in user directories or on external drives
  • Custom-compiled Chromium versions for specific testing requirements
  • Enterprise environments where browsers are installed in non-standard locations
  • Multiple browser versions side-by-side for compatibility testing

In these cases, explicitly setting the browser_executable_path parameter ensures Zendriver uses your intended binary.

How Zendriver Locates the Browser Binary

Understanding the internal flow helps debug path-related issues. Zendriver processes the executable path through three distinct layers:

The Configuration Layer

The Config class in zendriver/core/config.py (lines 66‑70) accepts browser_executable_path during instantiation. If omitted, the constructor calls find_executable() to locate the binary automatically.

from zendriver import Config

# Explicit path stored in Config instance

config = Config(browser_executable_path="/opt/chrome/chrome")

The Browser Creation Layer

Browser.create() in zendriver/core/browser.py (lines 75‑78) forwards the supplied browser_executable_path to the Config object. This method serves as the primary entry point for browser instantiation.

from zendriver import Browser

browser = await Browser.create(
    browser_executable_path="/usr/local/bin/brave"
)

Runtime Validation

When Browser.start() executes, Zendriver validates that the file exists at the specified path. If validation fails, the method raises an informative error indicating the file cannot be found. This check occurs in zendriver/core/browser.py (lines 336‑350).

Methods to Specify a Custom Browser Executable Path

Zendriver provides three APIs for passing the executable path, ranging from high-level helpers to low-level class methods.

Method 1: Using the High-Level zd.start() Helper

The simplest approach uses the zd.start() convenience function. Pass browser_executable_path as a keyword argument:

import asyncio
import zendriver as zd

async def main():
    browser = await zd.start(
        headless=False,
        browser_executable_path="/opt/custom-chrome/chrome",
        browser="chrome",  # optional: force Chrome instead of auto-detect

    )
    page = await browser.get("https://example.com")
    await page.save_screenshot()
    await browser.stop()

asyncio.run(main())

The helper forwards the argument to Browser.create, which constructs the underlying Config object.

Method 2: Creating a Reusable Config Object

For scenarios requiring multiple browser instances with the same settings, instantiate Config directly:

import asyncio
import zendriver as zd
from zendriver import Config

async def main():
    cfg = Config(
        headless=False,
        browser_executable_path="C:\\Users\\Me\\Portable\\Brave\\brave.exe",
        browser="brave",  # explicitly select Brave

    )

    # First browser instance

    b1 = await zd.start(config=cfg)
    await b1.get("https://github.com")
    await b1.stop()

    # Second browser instance reuses the same binary

    b2 = await zd.start(config=cfg)
    await b2.get("https://python.org")
    await b2.stop()

asyncio.run(main())

Creating a Config once avoids repeated path resolution and ensures consistent settings across instances.

Method 3: Direct Browser.create API

For maximum control, use the low-level Browser.create class method:

import asyncio
from zendriver import Browser

async def main():
    browser = await Browser.create(
        headless=False,
        browser_executable_path="/usr/local/bin/custom-chrome",
        browser="chrome",
    )
    page = await browser.get("https://docs.python.org/3/")
    await page.save_screenshot()
    await browser.stop()

asyncio.run(main())

This approach exposes all Browser.create parameters, including user_data_dir, sandbox, and lang.

Understanding the Fallback Mechanism

When browser_executable_path is None (the default), Zendriver invokes find_executable() defined in zendriver/core/config.py (lines 311‑393). This function builds a list of candidate binaries based on the requested browser parameter ("chrome", "brave", or "auto").

The function probes:

  • The system PATH environment variable
  • Known installation directories on Windows (Program Files, LocalAppData)
  • macOS bundle locations (/Applications, ~/Applications)

It returns the shortest existing path. If no binary is found, it raises a FileNotFoundError with a clear message suggesting the use of browser_executable_path.

Summary

  • Override auto-discovery by passing browser_executable_path to any Zendriver entry point.
  • Three API levels support custom paths: zd.start() for convenience, Config() for reusability, and Browser.create() for full control.
  • Validation occurs in Browser.start() at runtime, raising informative errors if the binary is missing.
  • Fallback logic in find_executable() searches standard directories only when no custom path is provided.

Frequently Asked Questions

What happens if the specified browser executable path does not exist?

Zendriver validates the executable path during Browser.start() in zendriver/core/browser.py (lines 336‑350). If the file does not exist or is not accessible, the library raises a FileNotFoundError with a descriptive message indicating the invalid path, allowing you to correct the configuration before retrying.

Can I use a portable version of Chrome with Zendriver?

Yes. Portable browser builds are a primary use case for the browser_executable_path parameter. Simply provide the absolute path to the portable binary (e.g., C:\Tools\ChromePortable\chrome.exe or /mnt/usb/chrome-linux/chrome) when calling zd.start() or creating a Config instance.

How does Zendriver choose between Chrome and Brave if I don't specify a browser?

When browser_executable_path is omitted and the browser parameter is set to "auto" (the default), Zendriver calls find_executable() in zendriver/core/config.py (lines 311‑393). This function searches for both Chrome and Brave binaries in standard installation directories and returns the first valid path found. If neither is found, it raises a FileNotFoundError.

Is it possible to specify the executable path via environment variables?

Zendriver does not natively check environment variables like BROWSER_EXECUTABLE_PATH. However, you can easily implement this pattern in your Python code by reading from os.environ and passing the value to the browser_executable_path parameter. This approach keeps your configuration external while maintaining explicit control over the binary selection.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →