santifer/career-ops · error · Error
--verify could not launch Chromium (run "npx playwright…
Error message
--verify could not launch Chromium (run "npx playwright install chromium" or re-run without --verify): ${err.message} What it means
After Playwright imports successfully, scan.mjs launches headless Chromium for --verify; if chromium.launch() rejects, this error is thrown. Unlike the import error, this means Playwright itself is present but the browser could not start (missing browser executable, missing system libraries, or sandbox restrictions).
Solutions
- Run `npx playwright install chromium` (add `--with-deps` on Linux to install system libraries)
- On bare containers, install OS deps: `npx playwright install-deps chromium` or use the official Playwright Docker image
- If the executable path is custom, set PLAYWRIGHT_BROWSERS_PATH or launch with executablePath
- Re-run the scan without --verify, which the message itself suggests as a fallback
Example fix
// before (Dockerfile) RUN npm ci CMD ["node", "scan.mjs", "--verify"] # launch fails: missing libs // after (Dockerfile) RUN npm ci && npx playwright install --with-deps chromium CMD ["node", "scan.mjs", "--verify"]
Defensive patterns
Strategy: try-catch
Validate before calling
let chromium;
try { ({ chromium } = await import('playwright')); } catch { /* handled elsewhere */ }
if (chromium) {
try { await chromium.launch({ headless: true }); console.log('chromium OK'); }
catch (e) { console.error('Chromium cannot launch:', e.message); }
} Try / catch
try {
browser = await chromium.launch({ headless: true });
} catch (err) {
if (err.message.includes('--verify could not launch') || /Executable doesn't exist|missing libraries/i.test(err.cause?.message ?? '')) {
console.error('Run `npx playwright install --with-deps chromium` or re-run without --verify');
process.exit(2);
}
throw err;
} Prevention
- Use `npx playwright install --with-deps chromium` in CI/Linux images so OS libraries are installed
- Set PLAYWRIGHT_BROWSERS_PATH explicitly in environments with restricted HOME/cache access
- Smoke-test chromium.launch() in a setup/doctor step before running long scans
- Prefer the official Playwright Docker image in containers
When it happens
Trigger: chromium.launch({ headless: true }) fails because Chromium binaries were never downloaded (`playwright install` skipped), host OS libraries are missing (common in minimal Docker images), or launch is blocked in a restricted sandbox/container.
Common situations: CI containers without libnss3/libatk etc.; environments with no HOME or no write access to the Playwright cache; running inside restricted Docker without --no-sandbox workarounds; partial browser download.
Understand the failure class
Background: "X is not installed. Please install it with pip install Y": missing optional dependency errors — ImportError/ValueError raised when a library's optional extra was never installed — this error's family across 22 libraries.
Related errors
- archive render skipped — no Playwright browser in env
- Blocked request to restricted destination (DNS)
- Blocked request to restricted destination (DNS)
- Blocked request to restricted destination
- Blocked request to restricted destination
AI-assisted analysis of santifer/career-ops@e7abd431fc (2026-09-16).
Data as JSON: /api/errors/9ceb89b61cbe7a54.
Report an issue: GitHub.
Appendix: source
Thrown at scan.mjs:2701
let createHeadedPageProvider;
let newLivenessPage;
let jitteredDelayMs;
let sleep;
try {
({ chromium } = await import('playwright'));
({ checkUrlLiveness, checkUrlLivenessWithFallback, createHeadedPageProvider, newLivenessPage, jitteredDelayMs, sleep } = await import('./liveness-browser.mjs'));
} catch (err) {
throw new Error(
`--verify requires Playwright with Chromium (run "npx playwright install chromium"): ${err.message}`,
{ cause: err },
);
}
let browser;
try {
browser = await chromium.launch({ headless: true });
} catch (err) {
throw new Error(
`--verify could not launch Chromium (run "npx playwright install chromium" or re-run without --verify): ${err.message}`,
{ cause: err },
);
}
// Three permanent buckets + one transient passthrough:
// verified → active pages and transient nav errors (retry next scan)
// expired → classifier-confirmed dead postings (HTTP 4xx, redirect markers,
// body patterns, listing pages, insufficient content)
// dropped → page loaded but classifier saw no Apply control. --verify is an
// opt-in stricter filter; keeping these defeats the purpose.
// invalid → up-front URL guard rejections (malformed / non-http / private)
const verified = [];
const expired = [];
const dropped = [];
const invalid = [];
const migrated = [];
View on GitHub (pinned to e7abd431fc)