n8n-io/n8n · error · BrowserExecutableNotFoundError
No executable path for ${browser}
Error message
No executable path for ${browser} What it means
Thrown as BrowserExecutableNotFoundError by the Playwright adapter's launch() (local mode) when resolvedConfig.browsers.get(config.browser) returns an entry with no executablePath (or returns undefined). Identical semantics to the agent-browser variant: the browser was either not detected or detected without a resolvable binary. The Playwright adapter then needs the path to execFile the browser with the extension connect URL.
Source
Thrown at packages/@n8n/mcp-browser/src/adapters/playwright.ts:140
// Local mode — connect to the user's running Chrome via extension bridge.
// The CDPRelayServer bridges Playwright ↔ Chrome extension (chrome.debugger).
this.relay = new CDPRelayServer();
const port = await this.relay.listen();
const extensionEndpoint = this.relay.extensionEndpoint(port);
// Open the extension's connect page with the relay URL so it auto-connects.
// `N8N_EVAL_AUTO_BROWSER_CONNECT=1` (set by the eval daemon spawn) appends
// `autoConnect=1` so the extension UI clicks Connect itself — keeps eval
// runs human-out-of-the-loop across reconnect cycles. The extension only
// honors `autoConnect=1` when the relay URL is localhost (which the relay
// always is — see `cdp-relay.ts`), so a crafted chrome-extension URL
// pointing at a remote relay can't trigger this path.
const autoConnect = process.env.N8N_EVAL_AUTO_BROWSER_CONNECT === '1';
const connectUrl = buildExtensionConnectUrl(extensionEndpoint, { autoConnect });
const browserInfo = this.resolvedConfig.browsers.get(config.browser);
const chromePath = browserInfo?.executablePath;
if (!chromePath) {
throw new BrowserExecutableNotFoundError(config.browser);
}
log.debug('launching browser:', chromePath);
log.debug('connect URL:', connectUrl);
// Launch the browser and detect early spawn failures (ENOENT, EACCES, etc.)
await new Promise<void>((resolve, reject) => {
const child = execFile(chromePath, [connectUrl]);
const earlyFailTimer = setTimeout(() => resolve(), 2_000);
child.on('error', (spawnError: Error) => {
clearTimeout(earlyFailTimer);
log.error('browser spawn error:', spawnError.message);
reject(new BrowserExecutableNotFoundError(`${config.browser} (${spawnError.message})`));
});
});
// Wait for the extension to connect and attach to tabs
log.debug('waiting for extension...');View on GitHub (pinned to 5ac6606e81)
Solutions
- Provide an explicit executablePath in the browser config for the selected browser.
- Re-run browser detection or rebuild the config after installing Chrome/Chromium.
- Verify the binary is executable by the gateway process user (chmod +x, check ownership).
- For headless/Docker, install chromium and point executablePath at it.
Example fix
// before
await playwrightAdapter.launch({ browser: 'chrome' });
// after — explicit path
await playwrightAdapter.launch({
browser: 'chrome',
executablePath: process.env.CHROME_PATH ?? '/usr/bin/chromium',
}); Defensive patterns
Strategy: validation
Validate before calling
function hasBrowserExecutable(config: ResolvedConfig, browser: string): boolean {
return Boolean(config.browsers.get(browser)?.executablePath);
} Type guard
function isBrowserExecutableAvailable(config: ResolvedConfig, browser: string): boolean {
return Boolean(config.browsers.get(browser)?.executablePath);
} Prevention
- Set an explicit executablePath in the browser config rather than relying on detection.
- Confirm the binary is executable by the gateway process user (no EACCES).
- In Docker/headless, install chromium and point executablePath at it.
When it happens
Trigger: Calling the Playwright adapter's launch(config) in local mode (extension-bridge mode, not remote/connect mode) where the selected browser's executablePath is missing. Also thrown a second way at line 153 when execFile(chromePath) emits an 'error' event (spawn failure like ENOENT/EACCES) within the 2-second early-fail window — that variant appends the spawn error message to the browser name.
Common situations: Chrome/Chromium not installed where the detector looks (non-standard path on Linux, app-not-installed on macOS); browser uninstalled after config was built; running in Docker/headless without a Chromium; the detected path points to a broken symlink; EACCES because the gateway process lacks execute permission on the binary.
Related errors
- No executable path for ${browser}
- No file chooser pending and no element target provided
- Model ID is required
- Invalid delegate sub-agent tool name "${name}": must start w
- ${toolName} requires resumeSubAgent and cancelSubAgent to be
AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12).
Data as JSON: /api/errors/0fb92972bf3f0fd6.
Report an issue: GitHub.