microsoft/playwright · critical · Error
Browser context was closed before the daemon could start
Error message
Browser context was closed before the daemon could start
What it means
Thrown by `startCliDaemonServer` after `BrowserBackend.initialize()` completes, if the supplied `BrowserContext` is already closed. The daemon cannot serve tools against a dead context, so it aborts during startup. This is a race-condition guard, not a normal flow.
Source
Thrown at packages/playwright-core/src/tools/cli-daemon/daemon.ts:77
}
): Promise<string> {
const sessionConfig = createSessionConfig(clientInfo, sessionName, browserInfo, options);
const { socketPath } = sessionConfig;
// Clean up existing socket file on Unix
if (process.platform !== 'win32' && await socketExists(socketPath)) {
try {
await fs.promises.unlink(socketPath);
} catch (error) {
throw error;
}
}
const backend = new BrowserBackend(contextConfig, browserContext, browserTools);
await backend.initialize(mcpClientInfo);
if (browserContext.isClosed())
throw new Error('Browser context was closed before the daemon could start');
const server = net.createServer(socket => {
const connection = new SocketConnection(socket);
const abortController = new AbortController();
connection.onclose = () => abortController.abort();
connection.onmessage = async message => {
const { id, method, params } = message;
try {
if (method === 'stop') {
await deleteSessionFile(clientInfo, sessionConfig);
const sendAck = async () => connection.send({ id, result: 'ok' }).catch(() => {});
if (options?.exitOnClose)
gracefullyProcessExitDoNotHang(0, () => sendAck());
else
await sendAck();
} else if (method === 'run') {
const { toolName, toolParams } = parseCliCommand(params.args);
toolParams._meta = { cwd: params.cwd, raw: params.raw || params.json, json: !!params.json };View on GitHub (pinned to c8fc3bf8d3)
Solutions
- Ensure the `BrowserContext` passed to `startCliDaemonServer` stays open until the server is listening (await the returned socket path before disposing).
- If the context is shared/attached, confirm the owning process is still alive and not itself shutting down.
- Move any `context.close()` / `browser.close()` calls to run only after the daemon has fully started (or stop the daemon first).
Example fix
// before
const ctx = await browser.newContext();
startCliDaemonServer('s', ctx, ...); // fire-and-forget
await ctx.close();
// after
const ctx = await browser.newContext();
await startCliDaemonServer('s', ctx, ...); // await startup
// ... use daemon ...
await ctx.close(); Defensive patterns
Strategy: try-catch
Validate before calling
// Before starting the daemon, ensure the context is alive.
function isContextUsable(ctx: any): boolean {
return !!ctx && typeof ctx.isClosed === 'function' && !ctx.isClosed();
} Try / catch
try {
await startCliDaemonServer(session, ctx, info, cfg, client, mcp, opts);
} catch (e) {
if (/Browser context was closed before the daemon could start/.test((e as Error).message)) {
// context died during init — restart browser/context then retry, or surface to caller
}
throw e;
} Prevention
- Await startCliDaemonServer before allowing any code path to close the context.
- Keep the context's owning process alive for the daemon's lifetime.
- Register the context close listener only after the daemon socket path is returned.
When it happens
Trigger: The browser/context is closed (manually, by a SIGINT handler, by the owning process exiting, or by a parallel `stop` call) in the window between `startCliDaemonServer` being called and the `isClosed()` check at daemon.ts:77. Also reachable if `createBrowserWithInfo` returns a context that is closed before the daemon server is stood up.
Common situations: Caller tears down the context immediately after requesting daemon start; a shared/attached browser's owner process exits mid-init; tests that start a daemon on a context fixture that is already disposed; an `exitOnClose` daemon whose context is closed by a prior `stop` arriving during boot.
Related errors
- Cannot find object to "${method}": ${guid}
- Unknown new child: ${params.guid}
- Cannot find parent object ${parentGuid} to create ${guid}
- Page has been closed.
- The page has closed
AI-assisted analysis of microsoft/playwright@c8fc3bf8d3 (2026-08-12).
Data as JSON: /api/errors/16721452ac098d87.
Report an issue: GitHub.