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
- Free up ephemeral ports / reduce parallel browser count on the host.
- Ensure the environment allows binding 127.0.0.1:0 (check sandbox/firewall rules).
- 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
- Reduce parallel browser/webview count to keep ephemeral ports available.
- Confirm the host/container allows binding 127.0.0.1:0.
- Report persistent failures as a Playwright environment issue.
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
- ios_webkit_debug_proxy ${proxyBase}/json returned ${res.stat
- No Mobile Safari tabs found at ${this._proxyBase}/json — ope
- Not supported
- Cannot set cookies without an open page
- Method not implemented.
AI-assisted analysis of microsoft/playwright@c8fc3bf8d3 (2026-08-12).
Data as JSON: /api/errors/60ffbc6fa76bd2f2.
Report an issue: GitHub.