n8n-io/n8n · error · BrowserExecutableNotFoundError

No executable path for

Error message

No executable path for ${browser}

What it means

Thrown as BrowserExecutableNotFoundError by AgentBrowserAdapter.launch when the resolved config has a browser entry for config.browser but its executablePath is undefined/empty. BrowserExecutableNotFoundError extends McpBrowserError and carries a hint telling the user to verify the browser is installed or provide an explicit executablePath. The message interpolates the browser name.

Solutions

  1. Provide an explicit executablePath in the browser config for the browser you are selecting.
  2. Re-run browser detection so the config reflects currently-installed browsers.
  3. Verify the browser is actually installed: run the binary path from a shell.
  4. If config.browser is wrong, pass the name of a browser that IS in resolvedConfig.browsers.

Example fix

// before — relying on auto-detection that returned no path
await adapter.launch({ browser: 'chrome' });

// after — set executablePath explicitly
const config = buildConfig({
  browsers: { chrome: { executablePath: '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome' } },
});
await adapter.launch({ browser: 'chrome' });
Defensive patterns

Strategy: validation

Validate before calling

function hasExecutablePath(config: ResolvedConfig, browser: string): boolean {
  return !!config.browsers.get(browser)?.executablePath;
}

Type guard

function isBrowserExecutableAvailable(config: ResolvedConfig, browser: string): boolean {
  return Boolean(config.browsers.get(browser)?.executablePath);
}

Prevention

When it happens

Trigger: Calling adapter.launch(config) where resolvedConfig.browsers.get(config.browser) returns an object with no executablePath, or returns undefined (so browserInfo?.executablePath is undefined). The browser was either not detected at all or detected without resolving a binary path.

Common situations: Chrome/Chromium/Brave/Edge was uninstalled after the config was built; the browser detection ran in a different environment (Docker vs host) and the path is stale; the browser is installed but not in a standard location the detector scans; config.browser names a browser that isn't in the resolved browsers map.

Related errors


AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12). Data as JSON: /api/errors/d8a68b793179bad0. Report an issue: GitHub.

Appendix: source

Thrown at packages/@n8n/mcp-browser/src/adapters/agent-browser.ts:184

				timeout: 5_000,
			});
		} catch {
			// no existing session — that's fine
		}
	}

	// =========================================================================
	// Lifecycle
	// =========================================================================

	async launch(config: ConnectConfig): Promise<void> {
		log.debug('launch: killing stale session');
		await this.killSession();
		log.debug('launch: stale session killed');

		const browserInfo = this.resolvedConfig.browsers.get(config.browser);
		const chromePath = browserInfo?.executablePath;
		if (!chromePath) throw new BrowserExecutableNotFoundError(config.browser);
		log.debug('launch: browser executable:', chromePath);

		if (!this.relay) {
			this.relay = new CDPRelayServer();
			this.relayPort = await this.relay.listen();
			log.debug('launch: relay listening on port', this.relayPort);
		} else {
			log.debug('launch: reusing existing relay on port', this.relayPort);
		}

		if (!this.relay.isExtensionConnected()) {
			const extensionEndpoint = this.relay.extensionEndpoint(this.relayPort);
			// `N8N_EVAL_AUTO_BROWSER_CONNECT=1` mirrors the playwright adapter — see
			// `playwright.ts` for the full justification. The extension only honors
			// the flag when the relay URL is localhost, which it always is here.
			const autoConnect = process.env.N8N_EVAL_AUTO_BROWSER_CONNECT === '1';
			const connectUrl =
				`chrome-extension://${BROWSER_USE_EXTENSION_ID}/connect.html` +

View on GitHub (pinned to 5ac6606e81)