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
- Run `npm install puppeteer` (or `bun add puppeteer`) in the project consuming the CLI so the dynamic import resolves.
- If you cannot install Chromium in the environment, pass options.browser — a puppeteer Browser you launched yourself — so the import is skipped entirely.
- Verify the install with `node -e "import('puppeteer')"` exiting cleanly before retrying the URL scan.
- 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
- Gate URL-scan features behind a canScanUrls() probe before exposing them in a UI.
- Document puppeteer as an optional peer dependency in your consuming package.
- Accept an options.browser injection point so CI can reuse one browser across scans.
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
- puppeteer is required for URL scanning. Install: npm install
- Browser script not found at ${browserScriptPath}
- Could not extract browser antipattern registry
- Created ${zipFileName} but it is 0 bytes.
- Chromium unavailable: ${err?.message || err}
AI-assisted analysis of pbakaus/impeccable@d14711ae3d (2026-08-13).
Data as JSON: /api/errors/f9ab2e8b1704f9e7.
Report an issue: GitHub.