microsoft/playwright · error · Error
Failed to launch ${this._name} because executable doesn't ex
Error message
Failed to launch ${this._name} because executable doesn't exist at ${executablePath} What it means
Thrown by resolveExecutablePath when options.executablePath is set (truthy) but existsAsync reports the path does not exist on disk. Playwright checks the custom path before falling back to the registry so a wrong explicit path fails fast rather than producing a confusing spawn error.
Source
Thrown at packages/playwright-core/src/server/browserType.ts:351
}
async prepareUserDataDir(options: types.LaunchOptions, userDataDir: string): Promise<void> {
}
supportsPipeTransport(options: types.LaunchOptions): boolean {
return true;
}
getExecutableName(options: types.LaunchOptions): string {
return options.channel || this._name;
}
protected async resolveExecutablePath(options: types.LaunchOptions): Promise<string | undefined> {
const { executablePath } = options;
if (!executablePath)
return undefined;
if (!(await existsAsync(executablePath)))
throw new Error(`Failed to launch ${this._name} because executable doesn't exist at ${executablePath}`);
return executablePath;
}
abstract defaultArgs(options: types.LaunchOptions, isPersistent: boolean, userDataDir: string): Promise<string[]>;
abstract connectToTransport(transport: ConnectionTransport, options: BrowserOptions, browserLogsCollector: RecentLogsCollector): Promise<Browser>;
abstract amendEnvironment(env: NodeJS.ProcessEnv, userDataDir: string, isPersistent: boolean, options: types.LaunchOptions): NodeJS.ProcessEnv;
abstract doRewriteStartupLog(logs: string): string;
abstract attemptToGracefullyCloseBrowser(transport: ConnectionTransport): void;
}
function copyTestHooks(from: object, to: object) {
for (const [key, value] of Object.entries(from)) {
if (key.startsWith('__testHook'))
(to as any)[key] = value;
}
}
View on GitHub (pinned to c8fc3bf8d3)
Solutions
- Verify the path with fs.existsSync before launching; print the absolute path you are passing.
- Point executablePath at the real binary, or remove it to let Playwright use the installed/registry browser.
- If the binary lives elsewhere per environment, resolve it from a validated env var with a fallback to the registry.
Example fix
// before
const b = await chromium.launch({ executablePath: process.env.BROWSER_BIN });
// after
const p = process.env.BROWSER_BIN;
if (p && !fs.existsSync(p)) throw new Error(`BROWSER_BIN not found: ${p}`);
const b = await chromium.launch({ executablePath: p }); Defensive patterns
Strategy: validation
Validate before calling
import fs from 'fs';
function resolveExecutable(p?: string): string | undefined {
if (!p) return undefined;
if (!fs.existsSync(p)) throw new Error(`executablePath does not exist: ${p}`);
return p;
}
// chromium.launch({ executablePath: resolveExecutable(process.env.BROWSER_BIN) }) Type guard
function executableExists(p?: string): boolean {
return !p || fs.existsSync(p);
} Prevention
- Always fs.existsSync-check an explicit executablePath before launch.
- Prefer letting Playwright resolve the registry binary by omitting executablePath.
When it happens
Trigger: chromium.launch({ executablePath: '/wrong/path/chrome' }) where the file is absent. Relative path that resolves against an unexpected cwd.
Common situations: Hardcoded path from another machine/container. Typo. Path computed from an env var that is unset. Cross-platform path separators. ARCH mismatch where the path exists for another OS.
Related errors
- Unsupported ${this._name} channel "${options.channel}"
- Failed to launch the browser process.\nBrowser logs:\n${upda
- Chromium distribution '${name}' is not supported on ${proces
- Chromium distribution '${name}' is not found${location}${ins
- Arguments can not specify page to be opened
AI-assisted analysis of microsoft/playwright@c8fc3bf8d3 (2026-08-12).
Data as JSON: /api/errors/99cc4682404fdbc5.
Report an issue: GitHub.