santifer/career-ops · error · Error

--liveness requires Playwright with Chromium (run "npx playw

Error message

--liveness requires Playwright with Chromium (run "npx playwright install chromium"): ${err.message}

What it means

filterLive() in scan-ats-full.mjs dynamically imports 'playwright' and './liveness-browser.mjs' to verify posting liveness via a headless browser. If the import fails (playwright package not installed or not resolvable), it throws an Error with a cause chain pointing to the underlying module error, and a remediation hint to install Chromium.

Source

Thrown at scan-ats-full.mjs:560

        // Lowest not-yet-finished index: everything below it is complete, so
        // a resumed run can restart exactly here without skipping work.
        const resumeAt = inFlight.size ? Math.min(...inFlight) : next;
        if (onItemDone) onItemDone({ done, resumeAt });
      }
    }
  }
  await Promise.all(Array.from({ length: Math.min(limit, items.length) }, () => worker()));
}

// ── Liveness verification (reuses liveness-browser.mjs) ────────────

async function filterLive(offers) {
  let chromium, checkUrlLiveness, newLivenessPage;
  try {
    ({ chromium } = await import('playwright'));
    ({ checkUrlLiveness, newLivenessPage } = await import('./liveness-browser.mjs'));
  } catch (err) {
    throw new Error(
      `--liveness requires Playwright with Chromium (run "npx playwright install chromium"): ${err.message}`,
      { cause: err },
    );
  }
  console.error(`\nVerifying liveness of ${offers.length} match(es) with Playwright (sequential)...`);
  const browser = await chromium.launch({ headless: true });
  const live = [];
  try {
    const page = await newLivenessPage(browser);
    // Sequential — project rule: never Playwright in parallel
    for (const offer of offers) {
      const { result, reason } = await checkUrlLiveness(page, offer.url);
      const icon = result === 'active' ? '✅' : result === 'expired' ? '❌' : '⚠️';
      console.error(`  ${icon} ${result.padEnd(9)} ${offer.company} | ${offer.title}${result === 'expired' ? ` (${reason})` : ''}`);
      if (result !== 'expired') live.push(offer); // keep 'uncertain' — transient errors retry next scan
    }
  } finally {
    await browser.close();

View on GitHub (pinned to 9b17a8ac97)

Solutions

  1. Install playwright: npm install playwright (or npm install -D playwright).
  2. Install the Chromium browser binary: npx playwright install chromium.
  3. If you do not need liveness checks, re-run without the --liveness flag.
  4. Verify the import resolves: node -e "import('playwright').then(() => console.log('ok'))".

Example fix

# before: --liveness used without playwright installed
node scan-ats-full.mjs --liveness

# after: install then run
npm install playwright && npx playwright install chromium
node scan-ats-full.mjs --liveness
Defensive patterns

Strategy: validation

Validate before calling

async function canImportPlaywright() {
  try { await import('playwright'); return true; }
  catch { return false; }
}
if (opts.liveness && !(await canImportPlaywright())) {
  throw new Error('Install playwright to use --liveness: npm install playwright && npx playwright install chromium');
}

Try / catch

try {
  await runScanWithLiveness();
} catch (err) {
  if (err.message.includes('requires Playwright')) {
    console.error(err.message);
    console.error('Falling back to non-liveness scan — re-run without --liveness.');
    opts.liveness = false;
    await runScanWithoutLiveness();
  } else throw err;
}

Prevention

When it happens

Trigger: Running `scan-ats-full.mjs --liveness` in an environment where the playwright npm package is not installed, not in node_modules, or the module resolution fails. The import('playwright') promise rejects.

Common situations: Fresh checkout without running npm install; a trimmed/Docker environment that excluded playwright to save space; a monorepo where playwright is a peer/optional dependency not hoisted; Node version incompatibility with the installed playwright.

Related errors


AI-assisted analysis of santifer/career-ops@9b17a8ac97 (2026-08-13). Data as JSON: /api/errors/8475fc9c8cbd26bd. Report an issue: GitHub.