{"record":{"id":"208d8ea19efd2630","repo":"santifer/career-ops","slug":"browser-cleanup-failed-err-message","errorCode":null,"errorMessage":"⚠️  Browser cleanup failed: ${err.message}","messagePattern":"⚠️  Browser cleanup failed: (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"generate-pdf.mjs","lineNumber":1646,"sourceCode":" *   reportNum?: string,\n *   inputPath?: string,\n *   workspaceRoot?: string,\n *   maxPages?: number,\n *   strictPages?: boolean,\n *   launchBrowser?: (options: {headless: boolean}) => Promise<import('playwright').Browser>\n * }} [opts]\n * @returns {Promise<{outputPath: string, pageCount: number, size: number}>}\n */\nexport async function renderHtmlToPdf(html, outputPath, opts = {}) {\n  const launchBrowser = opts.launchBrowser || ((options) => chromium.launch(options));\n  let browser = null;\n  try {\n    browser = await launchBrowser({ headless: true });\n    return await renderInPage(browser, html, outputPath, opts);\n  } finally {\n    if (browser) {\n      await browser.close().catch((err) => {\n        console.warn(`⚠️  Browser cleanup failed: ${err.message}`);\n      });\n    }\n  }\n}\n\n/**\n * Render one already-normalized HTML document to a PDF on an already-launched\n * browser. This is the page-level half of the render — it owns the per-document\n * work (theme/print/font injection, temp file, page, PDF, page-budget, manifest)\n * but NOT the browser lifecycle. Both the single-CV path (renderHtmlToPdf) and\n * the batch path (renderBatch) call this exact function, which is what keeps a\n * single-CV render byte-identical whether it runs alone or inside a batch (#2384).\n *\n * The page and the temp HTML file are always cleaned up in a finally, so a\n * throw here (e.g. a strict page-budget overflow) never leaks a page into the\n * shared browser — the caller's remaining documents keep their own fresh pages.\n *\n * @param {import('playwright').Browser} browser - An open browser to render on.","sourceCodeStart":1628,"sourceCodeEnd":1664,"githubUrl":"https://github.com/santifer/career-ops/blob/aac998c7ed7248ea853b720ceeb1fdbeb322fc5d/generate-pdf.mjs#L1628-L1664","documentation":"A non-fatal warning emitted when browser.close() on the Playwright browser instance rejects during the finally block of a single-document PDF render. The PDF may already have been produced; only teardown failed, typically because the browser process already died or the connection to it was lost.","triggerScenarios":"browser.close() rejects inside the finally of the render helper after launchBrowser({headless:true}) — Playwright browser crashed mid-render, connection closed because the Chromium process was killed (OOM, signal), or the target closed unexpectedly before cleanup.","commonSituations":"Chromium OOM-killed on large HTML during a long batch; headless Chromium crash from a missing system dependency; container sandbox killing zombie browser processes; calling browser.close() twice via overlapping code paths in a customized fork.","solutions":["Ignore if the PDF was written — this is teardown-only; verify the output file exists","Update Playwright (npx playwright install --with-deps) so the browser build matches the driver and stops crashing","Check memory limits (dmesg / container OOM logs) and reduce batch concurrency","If it recurs with no crash, wrap close with a browser.process kill fallback"],"exampleFix":"// before\nawait browser.close().catch((err) => console.warn(`⚠️  Browser cleanup failed: ${err.message}`));\n// after\nawait browser.close().catch((err) => {\n  if (!err.message.includes('Target closed') && !err.message.includes('Connection closed')) {\n    console.warn(`⚠️  Browser cleanup failed: ${err.message}`);\n  }\n  browser.process()?.kill('SIGKILL');\n});","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"function isBenignTeardownError(err) {\n  const msg = String(err?.message ?? '');\n  return /Target closed|Connection closed|Browser has been closed|Target crashed/i.test(msg);\n}","tryCatchPattern":"await browser.close().catch((err) => {\n  if (!isBenignTeardownError(err)) console.warn(`Browser cleanup failed: ${err.message}`);\n});","preventionTips":["Keep Playwright and its browser builds in sync (npx playwright install)","Give headless Chromium adequate memory in batch environments","Install OS dependencies (playwright install-deps) before running in CI/containers","Check the rendered output file exists before treating teardown warnings as failures"],"tags":["playwright","browser","cleanup","teardown"],"backgroundTag":"browser-cleanup-failed","analyzedSha":"aac998c7ed7248ea853b720ceeb1fdbeb322fc5d","analyzedAt":"2026-09-16T06:35:29.214Z","contentChangedAt":"2026-09-16T06:35:29.214Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}