santifer/career-ops · warning
⚠️ Context cleanup failed
Error message
⚠️ Context cleanup failed: ${err.message} What it means
Warning emitted when closing the per-document BrowserContext rejects. Each rendered document gets its own (JS-disabled) context so contexts do not accumulate in the shared browser across a batch (#2384); a failed close means the context may linger in memory until the browser itself closes.
Solutions
- Benign if the browser is closed right after in the same finally block — contexts die with it
- Reduce batch size / add periodic browser restarts to avoid mid-batch crashes
- Run npx playwright install to align driver and browser versions
- If contexts accumulate without warnings being the cause, check for code paths that skip the finally
Defensive patterns
Strategy: try-catch
Type guard
if (context && typeof context.close === 'function') { /* safe to close */ } Try / catch
await context.close().catch((err) => {
if (!/Target closed|Connection closed/i.test(err.message)) {
console.warn(`Context cleanup failed: ${err.message}`);
}
}); Prevention
- Close the per-document context in finally (as this code does) to prevent context accumulation (#2384)
- Avoid closing the same context from two code paths
- Keep driver and browser versions aligned
- Split very large batches so a late crash does not strand many contexts
When it happens
Trigger: context.close() rejects in the finally after render — the context was already detached because the browser died, the page inside it crashed taking the context down, or the connection to the browser process was severed mid-batch.
Common situations: Shared-browser batch rendering where an earlier document crashed Chromium; context closed concurrently elsewhere; Playwright version mismatch between driver and browser build.
Related errors
- ⚠️ Browser cleanup failed
- ⚠️ Page cleanup failed
- archive render skipped — no Playwright browser in env
- Blocked request to restricted destination (DNS)
- Blocked request to restricted destination (DNS)
AI-assisted analysis of santifer/career-ops@aac998c7ed (2026-09-16).
Data as JSON: /api/errors/3207bb399783749f.
Report an issue: GitHub.
Appendix: source
Thrown at generate-pdf.mjs:1806
// The PDF itself succeeded — never fail the run over manifest bookkeeping.
console.error(`⚠️ Manifest update failed: ${err.message}`);
}
return { outputPath, pageCount, size: pdfBuffer.length };
} finally {
// Close the page so a batch does not accumulate pages into the shared
// browser (leak → OOM). Optional-chained: the single path's browser.close()
// already reclaims the page, and minimal test doubles may omit close().
if (page && typeof page.close === 'function') {
await page.close().catch((err) => {
console.warn(`⚠️ Page cleanup failed: ${err.message}`);
});
}
// Close the per-document context too, so the JS-disabled context created
// above does not accumulate in the shared browser across a batch (#2384).
if (context && typeof context.close === 'function') {
await context.close().catch((err) => {
console.warn(`⚠️ Context cleanup failed: ${err.message}`);
});
}
// Clean up temp file
await unlink(tmpHtmlPath).catch((err) => {
if (err?.code !== 'ENOENT') {
console.warn(`⚠️ Temporary HTML cleanup failed: ${err.message}`);
}
});
}
}
/**
* Render many already-normalized HTML documents through ONE shared Chromium.
*
* Maintainer conditions (#2384): the browser is launched once via the same
* opts.launchBrowser seam the single path uses, and closed in a finally at the
* batch boundary — it never outlives the batch and is torn down even if a
* document throws. Each entry renders on its own page (renderInPage), and aView on GitHub (pinned to aac998c7ed)