pbakaus/impeccable · error

puppeteer is required for URL scanning. Install: npm install

Error message

puppeteer is required for URL scanning. Install: npm install puppeteer

What it means

Thrown by detectUrl() when the optional 'puppeteer' dependency cannot be dynamically imported, and the caller did not supply a pre-launched browser via options.browser. Puppeteer is a peer/optional dependency for URL scanning because the CLI's file/HTML path is the primary mode and bundling Chromium would bloat every install. The error fires only for the URL (live-site) detection code path.

Source

Thrown at cli/engine/engines/browser/detect-url.mjs:181

// ---------------------------------------------------------------------------

async function detectUrl(url, options = {}) {
  const profile = options?.profile;
  const waitUntil = options?.waitUntil || 'networkidle0';
  const settleMs = Number.isFinite(options?.settleMs) ? options.settleMs : 0;
  const viewport = options?.viewport || { width: 1280, height: 800 };
  const externalBrowser = options?.browser || null;
  let puppeteer;
  if (!externalBrowser) {
    try {
      puppeteer = await profileStepAsync(profile, {
        engine: 'browser',
        phase: 'setup',
        ruleId: 'import-puppeteer',
        target: url,
      }, () => import('puppeteer'));
    } catch {
      throw new Error('puppeteer is required for URL scanning. Install: npm install puppeteer');
    }
  }

  // Read the browser detection script — reuse it instead of reimplementing
  const browserScriptPath = path.resolve(
    path.dirname(fileURLToPath(import.meta.url)),
    '..',
    '..',
    'detect-antipatterns-browser.js'
  );
  let browserScript;
  try {
    browserScript = profileStep(profile, {
      engine: 'browser',
      phase: 'setup',
      ruleId: 'read-browser-script',
      target: url,
    }, () => fs.readFileSync(browserScriptPath, 'utf-8'));

View on GitHub (pinned to d14711ae3d)

Solutions

  1. Run `npm install puppeteer` (or `bun add puppeteer`) in the project consuming the CLI so the dynamic import resolves.
  2. If you cannot install Chromium in the environment, pass options.browser — a puppeteer Browser you launched yourself — so the import is skipped entirely.
  3. Verify the install with `node -e "import('puppeteer')"` exiting cleanly before retrying the URL scan.
  4. In monorepos, ensure puppeteer is resolvable from the cli/engine/engines/browser/ directory (install at the workspace root or in the package that runs the CLI).

Example fix

// before
const results = await detectUrl('https://example.com');

// after — install puppeteer, OR inject an existing browser
const puppeteer = await import('puppeteer');
const browser = await puppeteer.launch({ headless: true });
const results = await detectUrl('https://example.com', { browser });
Defensive patterns

Strategy: validation

Validate before calling

async function canScanUrls() {
  try { await import('puppeteer'); return true; }
  catch { return false; }
}
// or check for an externally provided browser:
const hasBrowser = Boolean(options.browser);

Type guard

/** True if the caller can run detectUrl without installing puppeteer. */
function hasUrlScanRuntime(options = {}) {
  return Boolean(options?.browser); // puppeteer availability is async-checked separately
}

Try / catch

try {
  const results = await detectUrl(url, options);
} catch (err) {
  if (/puppeteer is required/.test(err.message)) {
    console.error('Install puppeteer or pass options.browser to scan URLs.');
  } else throw err;
}

Prevention

When it happens

Trigger: Calling detectUrl(url) (or the CLI's URL-scan subcommand) without 'puppeteer' installed in node_modules AND without passing options.browser as an already-launched puppeteer Browser instance. The dynamic import('puppeteer') rejects, the catch re-throws this exact message.

Common situations: Running the impeccable CLI in a minimal install that excluded optional deps; CI where puppeteer was pruned to save space; a monorepo where puppeteer is hoisted to a parent node_modules the dynamic import cannot resolve; using a URL target for the first time after only testing local files.

Related errors


AI-assisted analysis of pbakaus/impeccable@d14711ae3d (2026-08-13). Data as JSON: /api/errors/f9ab2e8b1704f9e7. Report an issue: GitHub.