microsoft/playwright · error · Error
Server is already started.
Error message
Server is already started.
What it means
`BrowserServer.start` sets `_isStarted=true` and refuses a second invocation. BrowserServer backs `browserType.launchServer` / connect endpoints and is meant to be started exactly once per instance.
Source
Thrown at packages/playwright-core/src/server/browser.ts:220
if (this.isConnected())
await progress.race(new Promise(x => this.once(Browser.Events.Disconnected, x)));
}
}
export class BrowserServer {
private _browser: Browser;
private _pipeServer?: PlaywrightPipeServer;
private _wsServer?: PlaywrightWebSocketServer;
private _pipeSocketPath?: string;
private _isStarted = false;
constructor(browser: Browser) {
this._browser = browser;
}
async start(title: string, options: channels.BrowserStartServerOptions): Promise<{ endpoint: string }> {
if (this._isStarted)
throw new Error(`Server is already started.`);
this._isStarted = true;
let endpoint: string;
if (options.host !== undefined || options.port !== undefined) {
this._wsServer = new PlaywrightWebSocketServer(this._browser, '/' + createGuid());
endpoint = await this._wsServer.listen(options.port ?? 0, options.host);
} else {
this._pipeServer = new PlaywrightPipeServer(this._browser);
this._pipeSocketPath = await this._socketPath();
await this._pipeServer.listen(this._pipeSocketPath);
endpoint = this._pipeSocketPath;
}
const browserInfo: BrowserInfo = {
guid: this._browser.guid,
browserName: this._browser.options.browserType,
launchOptions: asClientLaunchOptions(this._browser.options.originalLaunchOptions),
userDataDir: this._browser.options.userDataDir,View on GitHub (pinned to c8fc3bf8d3)
Solutions
- Track started state yourself and only call start() once
- Call `stop()` then create a new BrowserServer rather than re-starting
- If you need a fresh endpoint, create a new instance instead of reusing
Example fix
// before
await server.start('t', {});
await server.start('t', {}); // throws
// after
await server.start('t', {});
// ... later
await server.stop();
server = browserType.launchServer();
await server.start('t', {}); Defensive patterns
Strategy: validation
Validate before calling
if (serverStarted) {
throw new Error('BrowserServer already started; stop() and create a new one to restart.');
}
await server.start(title, opts);
serverStarted = true; Prevention
- Track started state in your own variable rather than relying on the throw
- Create a new BrowserServer after stop() instead of reusing the instance
- Guard startup in lifecycle hooks against double-invocation
When it happens
Trigger: Calling `browserServer.start(...)` twice on the same BrowserServer instance.
Common situations: Reuse logic that inadvertently starts a server twice; warm-start optimization that re-runs startup; buggy lifecycle management that re-invokes start on reconnect.
Related errors
- The object has been collected to prevent unbounded heap grow
- Cannot find object to "${method}": ${guid}
- Unknown new child: ${params.guid}
- Cannot find parent object ${parentGuid} to create ${guid}
- Cannot set input files to detached element
AI-assisted analysis of microsoft/playwright@c8fc3bf8d3 (2026-08-12).
Data as JSON: /api/errors/9abea635f6540d94.
Report an issue: GitHub.