{"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":1448,"sourceCode":" *   baseDir?: string,\n *   reportNum?: string,\n *   inputPath?: 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":1430,"sourceCodeEnd":1466,"githubUrl":"https://github.com/santifer/career-ops/blob/60398d6549a46f5266929538af21cfab94badc75/generate-pdf.mjs#L1430-L1466","documentation":"Best-effort cleanup warning in generate-pdf.mjs's renderHtmlToPdf(): after renderInPage() finishes (or throws), browser.close() rejected and the rejection is only logged, never rethrown — a failed close must not mask a successful PDF render. Typical when Chromium already died (crash/OOM) or was closed twice.","triggerScenarios":"Calling renderHtmlToPdf() where the launchBrowser() chromium instance crashed mid-render (OOM-killed headless process), or a test double/outer harness already closed the browser before the finally ran. The render result is unaffected unless the crash itself failed the render earlier.","commonSituations":"Memory pressure in batch/parallel CV rendering killing Chromium; custom launchBrowser injection in tests that closes eagerly; CI sandboxes with low memory limits.","solutions":["Treat the warning as noise if the PDF was written — check the returned outputPath/pageCount","For repeated occurrences in batches, reduce parallelism or raise memory so Chromium stops dying","If injecting a custom launchBrowser, do not close the browser yourself; let this function own the lifecycle","Confirm no double-close: renderHtmlToPdf owns close(); renderInPage callers must only close pages/contexts they created"],"exampleFix":null,"handlingStrategy":"fallback","validationCode":"// Before a batch, sanity-check that Chromium can launch at all\nconst browser = await (opts.launchBrowser || ((o) => chromium.launch(o)))({ headless: true });\nawait browser.close().catch(() => {}); // probe succeeded if we got here","typeGuard":null,"tryCatchPattern":"// The function already swallows close() rejections; trust the return value:\ntry {\n  const { outputPath, pageCount } = await renderHtmlToPdf(html, out, opts);\n  // success — a cleanup warning afterwards is noise\n} catch (err) {\n  // real render failure; browser cleanup already handled best-effort\n  throw err;\n}","preventionTips":["Trust {outputPath, pageCount} over cleanup warnings — a failed close never marks a render failed","Let renderHtmlToPdf own browser.close(); never close the browser you passed via launchBrowser yourself","Cap batch parallelism and memory so Chromium stops getting OOM-killed mid-run"],"tags":["playwright","cleanup","browser-lifecycle","best-effort"],"backgroundTag":"resource-cleanup-failed","analyzedSha":"60398d6549a46f5266929538af21cfab94badc75","analyzedAt":"2026-08-20T23:00:06.764Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}