{"record":{"id":"69f88adbc0b69a57","repo":"microsoft/playwright","slug":"pipe-has-been-closed","errorCode":null,"errorMessage":"Pipe has been closed","messagePattern":"Pipe has been closed","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/playwright-core/src/server/pipeTransport.ts","lineNumber":59,"sourceCode":"    });\n    pipeRead.on('error', e => debugLogger.log('error', e));\n    pipeWrite.on('error', e => debugLogger.log('error', e));\n    this.onmessage = undefined;\n  }\n\n  get onclose() {\n    return this._onclose;\n  }\n\n  set onclose(onclose: undefined | ((reason?: string) => void)) {\n    this._onclose = onclose;\n    if (onclose && !this._pipeRead.readable)\n      onclose();\n  }\n\n  send(message: ProtocolRequest) {\n    if (this._closed)\n      throw new Error('Pipe has been closed');\n    this._pipeWrite.write(JSON.stringify(message));\n    this._pipeWrite.write('\\0');\n  }\n\n  close() {\n    throw new Error('unimplemented');\n  }\n\n  _dispatch(buffer: Buffer) {\n    let end = buffer.indexOf('\\0');\n    if (end === -1) {\n      this._pendingBuffers.push(buffer);\n      return;\n    }\n    this._pendingBuffers.push(buffer.slice(0, end));\n    const message = Buffer.concat(this._pendingBuffers).toString();\n    this._waitForNextTask(() => {\n      if (this.onmessage)","sourceCodeStart":41,"sourceCodeEnd":77,"githubUrl":"https://github.com/microsoft/playwright/blob/c8fc3bf8d31542d59b4d4d9eaab1df93ff541dc6/packages/playwright-core/src/server/pipeTransport.ts#L41-L77","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Treat browser process exit as terminal: stop issuing commands once the browser/Page close event has fired.","Ensure no fire-and-forget commands outlive the browser; await all operations before calling browser.close().","Investigate the actual browser crash (check stderr/stdout, system logs, dmesg for OOM) if the close was unexpected."],"exampleFix":"// before\nbrowser.close(); // browser exits\nawait page.title(); // PipeTransport.send -> Pipe has been closed\n// after\nconst title = await page.title();\nawait browser.close();","handlingStrategy":"try-catch","validationCode":"if (browser.isConnected() === false) throw new Error('browser not connected');","typeGuard":null,"tryCatchPattern":"try {\n  await page.title();\n} catch (e) {\n  if (/Pipe has been closed/.test(e.message)) {\n    // browser process is gone; recreate the browser rather than retrying commands\n    throw new Error('Browser process exited unexpectedly');\n  }\n  throw e;\n}","preventionTips":["Check browser.isConnected()/page.isClosed() before issuing commands when lifecycle is uncertain.","Await all operations before calling browser.close().","Investigate unexpected browser exits (OOM, crashes) via stderr and system logs."],"tags":["transport","pipe","browser-crash","lifecycle","protocol"],"backgroundTag":null,"analyzedSha":"c8fc3bf8d31542d59b4d4d9eaab1df93ff541dc6","analyzedAt":"2026-08-12T07:26:36.950Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}