{"record":{"id":"0fb92972bf3f0fd6","repo":"n8n-io/n8n","slug":"no-executable-path-for-browser-0fb929","errorCode":null,"errorMessage":"No executable path for ${browser}","messagePattern":"No executable path for (.+?)","errorType":"exception","errorClass":"BrowserExecutableNotFoundError","httpStatus":null,"severity":"error","filePath":"packages/@n8n/mcp-browser/src/adapters/playwright.ts","lineNumber":140,"sourceCode":"\t\t// Local mode — connect to the user's running Chrome via extension bridge.\n\t\t// The CDPRelayServer bridges Playwright ↔ Chrome extension (chrome.debugger).\n\t\tthis.relay = new CDPRelayServer();\n\t\tconst port = await this.relay.listen();\n\t\tconst extensionEndpoint = this.relay.extensionEndpoint(port);\n\n\t\t// Open the extension's connect page with the relay URL so it auto-connects.\n\t\t// `N8N_EVAL_AUTO_BROWSER_CONNECT=1` (set by the eval daemon spawn) appends\n\t\t// `autoConnect=1` so the extension UI clicks Connect itself — keeps eval\n\t\t// runs human-out-of-the-loop across reconnect cycles. The extension only\n\t\t// honors `autoConnect=1` when the relay URL is localhost (which the relay\n\t\t// always is — see `cdp-relay.ts`), so a crafted chrome-extension URL\n\t\t// pointing at a remote relay can't trigger this path.\n\t\tconst autoConnect = process.env.N8N_EVAL_AUTO_BROWSER_CONNECT === '1';\n\t\tconst connectUrl = buildExtensionConnectUrl(extensionEndpoint, { autoConnect });\n\t\tconst browserInfo = this.resolvedConfig.browsers.get(config.browser);\n\t\tconst chromePath = browserInfo?.executablePath;\n\t\tif (!chromePath) {\n\t\t\tthrow new BrowserExecutableNotFoundError(config.browser);\n\t\t}\n\n\t\tlog.debug('launching browser:', chromePath);\n\t\tlog.debug('connect URL:', connectUrl);\n\n\t\t// Launch the browser and detect early spawn failures (ENOENT, EACCES, etc.)\n\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\tconst child = execFile(chromePath, [connectUrl]);\n\t\t\tconst earlyFailTimer = setTimeout(() => resolve(), 2_000);\n\t\t\tchild.on('error', (spawnError: Error) => {\n\t\t\t\tclearTimeout(earlyFailTimer);\n\t\t\t\tlog.error('browser spawn error:', spawnError.message);\n\t\t\t\treject(new BrowserExecutableNotFoundError(`${config.browser} (${spawnError.message})`));\n\t\t\t});\n\t\t});\n\n\t\t// Wait for the extension to connect and attach to tabs\n\t\tlog.debug('waiting for extension...');","sourceCodeStart":122,"sourceCodeEnd":158,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/@n8n/mcp-browser/src/adapters/playwright.ts#L122-L158","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nawait playwrightAdapter.launch({ browser: 'chrome' });\n\n// after — explicit path\nawait playwrightAdapter.launch({\n  browser: 'chrome',\n  executablePath: process.env.CHROME_PATH ?? '/usr/bin/chromium',\n});","handlingStrategy":"validation","validationCode":"function hasBrowserExecutable(config: ResolvedConfig, browser: string): boolean {\n  return Boolean(config.browsers.get(browser)?.executablePath);\n}","typeGuard":"function isBrowserExecutableAvailable(config: ResolvedConfig, browser: string): boolean {\n  return Boolean(config.browsers.get(browser)?.executablePath);\n}","tryCatchPattern":null,"preventionTips":["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."],"tags":["mcp-browser","playwright","browser-launch","configuration"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}