santifer/career-ops · error · Error

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

Error message

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

What it means

filterLive verifies matched offers with a headless Chromium browser. The library throws this when the dynamic import of 'playwright' or './liveness-browser.mjs' fails, i.e. Playwright is not installed or its Chromium browser binary is missing. The thrown error chains the original import failure as `cause`.

Solutions

  1. Run `npx playwright install chromium` to download the browser binary
  2. Ensure playwright is a project dependency: `npm install playwright`
  3. If liveness-browser.mjs is missing, re-clone or restore the repo files
  4. Re-run the scan without --liveness if browser verification is not needed

Example fix

// shell, before
node scan-ats-full.mjs --liveness   # Error: requires Playwright with Chromium
// after
npm install playwright
npx playwright install chromium
node scan-ats-full.mjs --liveness
Defensive patterns

Strategy: fallback

Validate before calling

// run before using --liveness
let playwrightOk = true;
try { await import('playwright'); } catch { playwrightOk = false; }
if (!playwrightOk) console.warn('--liveness unavailable: run npx playwright install chromium');

Try / catch

try {
  await runWithLiveness(offers);
} catch (err) {
  if (err.message.includes('--liveness requires Playwright')) {
    console.warn('Skipping liveness check:', err.cause?.message ?? err.message);
    return offers; // degrade gracefully
  }
  throw err;
}

Prevention

When it happens

Trigger: Running scan-ats-full.mjs with the --liveness flag in an environment where playwright is not an installed npm dependency, or playwright is installed but `npx playwright install chromium` was never run (browser binaries absent), or liveness-browser.mjs is missing from the repo.

Common situations: Fresh clone / CI container without post-install browser download; running with a stripped node_modules; using a Node version incompatible with the installed Playwright build; global vs local install mismatch.

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


AI-assisted analysis of santifer/career-ops@e7abd431fc (2026-09-16). Data as JSON: /api/errors/8475fc9c8cbd26bd. Report an issue: GitHub.

Appendix: source

Thrown at scan-ats-full.mjs:675

        // 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 e7abd431fc)