microsoft/playwright · error · Error

SyncServer: failed to bind HTTP server

Error message

SyncServer: failed to bind HTTP server

What it means

SyncServer.start() binds a tiny loopback HTTP server (listen 0 on 127.0.0.1) for page-side synchronous XHR callbacks during webview automation. After listen resolves, it reads server.address(); if the address is missing or a string (pipe name) instead of an {port} object, the bind is considered failed and the server cannot be used. This is an internal startup failure for the iOS/Safari webview path.

Source

Thrown at packages/playwright-core/src/server/webkit/webview/syncServer.ts:52

// the endpoint cannot be discovered by a script running in the page just by
// knowing the (randomly bound) port.
export class SyncServer {
  private readonly _server: Server;
  private readonly _baseUrl: string;
  private readonly _handlers = new Map<string, SyncHandler>();

  static async start(): Promise<SyncServer> {
    const server = createHttpServer();
    await new Promise<void>((resolve, reject) => {
      server.once('error', reject);
      server.listen(0, '127.0.0.1', () => {
        server.removeListener('error', reject);
        resolve();
      });
    });
    const address = server.address();
    if (!address || typeof address === 'string')
      throw new Error('SyncServer: failed to bind HTTP server');
    return new SyncServer(server, `http://127.0.0.1:${address.port}`);
  }

  private constructor(server: Server, baseUrl: string) {
    this._server = server;
    this._baseUrl = baseUrl;
    this._server.on('request', (req, res) => this._handleRequest(req, res));
  }

  addHandler(handler: SyncHandler): SyncHandlerRegistration {
    const guid = createGuid();
    this._handlers.set(guid, handler);
    return {
      endpoint: `${this._baseUrl}/${guid}`,
      dispose: () => this._handlers.delete(guid),
    };
  }

View on GitHub (pinned to c8fc3bf8d3)

Solutions

  1. Free up ephemeral ports / reduce parallel browser count on the host.
  2. Ensure the environment allows binding 127.0.0.1:0 (check sandbox/firewall rules).
  3. Retry the connectOverCDP/webview connection; if it persists, report a Playwright bug with the host networking details.
Defensive patterns

Strategy: retry

Validate before calling

// No deterministic caller guard; ensure loopback TCP is bindable.
// Check ephemeral port availability on the host before launching webview automation.

Try / catch

for (let attempt = 0; attempt < 3; attempt++) {
  try { return await startWebviewSession(); }
  catch (e) {
    if (!/SyncServer: failed to bind/i.test(String(e.message)) || attempt === 2) throw e;
    await new Promise(r => setTimeout(r, 500));
  }
}

Prevention

When it happens

Trigger: Constructing a WVBrowser/SyncServer when the OS refuses to assign a loopback TCP port, or when the server somehow ends up bound to a pipe/unix socket yielding a string address. Essentially an environment where binding a random loopback port is impossible.

Common situations: Heavy process exhaustion (no ephemeral ports available); a sandbox/container that blocks loopback TCP listen; a corrupted Node http server state. Not user-facing in normal Playwright flows.

Related errors


AI-assisted analysis of microsoft/playwright@c8fc3bf8d3 (2026-08-12). Data as JSON: /api/errors/60ffbc6fa76bd2f2. Report an issue: GitHub.