santifer/career-ops · critical · Error

The apply feature needs Google Chrome. Install Chrome (or ru

Error message

The apply feature needs Google Chrome. Install Chrome (or run: npx playwright install chromium) and try again.

What it means

Thrown when the headed-browser bootstrap in session.ts cannot launch ANY usable Chromium-based browser. The code first tries Google Chrome via Playwright's `channel: 'chrome'` launch; on failure it retries with Playwright's bundled Chromium (no channel). Only if BOTH launches fail does it throw, because the apply feature genuinely requires a headed browser to drive a real form. This is a hard environment prerequisite, not a transient runtime fault.

Source

Thrown at web/src/lib/apply/session.ts:141

const SESSIONS: Map<string, Session> = (globalThis.__coApplySessions ??= new Map());

async function headedBrowser(): Promise<Browser> {
  const b = globalThis.__coHeadedBrowser;
  if (b && b.isConnected()) return b;
  let nb: Browser;
  try {
    nb = await chromium.launch({
      channel: "chrome",
      headless: false,
      args: ["--window-position=-3200,-3200", "--window-size=1280,940"], // off-screen during fill; moved on-screen at handoff
    });
  } catch {
    // No system Google Chrome → fall back to Playwright's bundled Chromium if
    // present; otherwise a clear, actionable error.
    try {
      nb = await chromium.launch({ headless: false, args: ["--window-position=-3200,-3200", "--window-size=1280,940"] });
    } catch {
      throw new Error("The apply feature needs Google Chrome. Install Chrome (or run: npx playwright install chromium) and try again.");
    }
  }
  globalThis.__coHeadedBrowser = nb;
  return nb;
}

/** Close the headed Chrome once no sessions have been active for a while, so we
 *  don't leak a browser process. Re-armed on every prune/close; cancelled on open. */
function scheduleIdleClose() {
  if (globalThis.__coIdleTimer) clearTimeout(globalThis.__coIdleTimer);
  globalThis.__coIdleTimer = setTimeout(() => {
    if (SESSIONS.size === 0) {
      const b = globalThis.__coHeadedBrowser;
      globalThis.__coHeadedBrowser = undefined;
      void b?.close().catch(() => {});
    }
  }, 5 * 60_000);
}

View on GitHub (pinned to 9b17a8ac97)

Solutions

  1. Run `npx playwright install chromium` in the project root to fetch Playwright's bundled Chromium (no system Chrome needed).
  2. Install Google Chrome system-wide (Debian/Ubuntu: `wget` the .deb + `sudo apt install ./google-chrome-stable_current_amd64.deb`; macOS: download from google.com/chrome).
  3. On Linux, also install Playwright's system deps: `npx playwright install-deps chromium` (libnss3, libatk1.0, libgbm, etc.).
  4. Verify the install with `npx playwright --version` and a smoke test (`npx playwright open about:blank`) before retrying `openSession`.
  5. In headless CI where the feature is unsupported, skip the apply route or gate it behind an env check rather than retrying the same launch.

Example fix

// before — no browser bootstrap in setup
npm install
// after — fetch the browser as part of setup
npm install && npx playwright install chromium && npx playwright install-deps chromium
Defensive patterns

Strategy: validation

Validate before calling

// Run during install / boot, before openSession is reachable.
import { chromium } from 'playwright';
async function canLaunchHeaded() {
  for (const opts of [
    { channel: 'chrome', headless: false },
    { headless: false }, // bundled chromium
  ]) {
    try {
      const b = await chromium.launch(opts);
      await b.close();
      return true;
    } catch {}
  }
  return false;
}
if (!await canLaunchHeaded()) {
  console.error('Run: npx playwright install chromium  (and install-deps on Linux)');
  process.exit(1);
}

Try / catch

try {
  const { id } = await openSession(url);
} catch (e) {
  if (/needs Google Chrome/.test(e.message)) {
    // surface install instructions to the user; do NOT auto-retry the same launch
    throw new Error('Browser missing. Run `npx playwright install chromium` then retry.');
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling `openSession()` (which calls `headedBrowser()`) on a machine where (a) Google Chrome is not installed AND (b) Playwright's bundled Chromium has not been downloaded via `npx playwright install chromium`. A second `chromium.launch()` with no channel fails (e.g. `Error: browserType.launch: Executable doesn't exist`), pushing execution into the inner catch where this Error is constructed.

Common situations: Fresh CI/container/VM with Node installed but no browser; first run after cloning on a new OS; Docker image missing Chrome; `npx playwright install` skipped in setup docs; Chrome uninstalled or moved; Linux server without GUI libs (libnss3, libatk, etc.) where even bundled Chromium can't start.

Related errors


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