microsoft/playwright · error · Error

Pipe has been closed

Error message

Pipe has been closed

What it means

Thrown by PipeTransport.send when a protocol message is written after the pipe has been marked closed. PipeTransport is the stdio-based channel between Playwright and a browser process launched with a pipe; once the read side emits 'close' (browser exited/crashed), any further send is rejected.

Source

Thrown at packages/playwright-core/src/server/pipeTransport.ts:59

    });
    pipeRead.on('error', e => debugLogger.log('error', e));
    pipeWrite.on('error', e => debugLogger.log('error', e));
    this.onmessage = undefined;
  }

  get onclose() {
    return this._onclose;
  }

  set onclose(onclose: undefined | ((reason?: string) => void)) {
    this._onclose = onclose;
    if (onclose && !this._pipeRead.readable)
      onclose();
  }

  send(message: ProtocolRequest) {
    if (this._closed)
      throw new Error('Pipe has been closed');
    this._pipeWrite.write(JSON.stringify(message));
    this._pipeWrite.write('\0');
  }

  close() {
    throw new Error('unimplemented');
  }

  _dispatch(buffer: Buffer) {
    let end = buffer.indexOf('\0');
    if (end === -1) {
      this._pendingBuffers.push(buffer);
      return;
    }
    this._pendingBuffers.push(buffer.slice(0, end));
    const message = Buffer.concat(this._pendingBuffers).toString();
    this._waitForNextTask(() => {
      if (this.onmessage)

View on GitHub (pinned to c8fc3bf8d3)

Solutions

  1. Treat browser process exit as terminal: stop issuing commands once the browser/Page close event has fired.
  2. Ensure no fire-and-forget commands outlive the browser; await all operations before calling browser.close().
  3. Investigate the actual browser crash (check stderr/stdout, system logs, dmesg for OOM) if the close was unexpected.

Example fix

// before
browser.close(); // browser exits
await page.title(); // PipeTransport.send -> Pipe has been closed
// after
const title = await page.title();
await browser.close();
Defensive patterns

Strategy: try-catch

Validate before calling

if (browser.isConnected() === false) throw new Error('browser not connected');

Try / catch

try {
  await page.title();
} catch (e) {
  if (/Pipe has been closed/.test(e.message)) {
    // browser process is gone; recreate the browser rather than retrying commands
    throw new Error('Browser process exited unexpectedly');
  }
  throw e;
}

Prevention

When it happens

Trigger: The browser process exited (crash, OOM, or explicit kill) while Playwright still had pending protocol requests; an operation is attempted on a Browser/Page after browser.close() or after the process died; a background task sends a command after teardown.

Common situations: Browser crashed due to resource exhaustion; test teardown raced with an in-flight command; the process was killed externally (CI timeout, container OOM); launching with pipe channel on an unstable environment.

Related errors


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