heygen-com/hyperframes · critical

Failed to load @puppeteer/browsers: ${cause} Fix: run `npm i

Error message

Failed to load @puppeteer/browsers: ${cause}
Fix: run `npm install` or `bun install` to restore missing packages, then retry.

What it means

Thrown by loadPuppeteerBrowsers() when the dynamic import('@puppeteer/browsers') fails. The CLI lazily imports this package only when it needs to download/manage chrome-headless-shell; if the package is missing from node_modules (incomplete install, monorepo hoist issue, corrupted node_modules), the import rejects and is rewrapped with a restore hint. The underlying cause message is included.

Source

Thrown at packages/cli/src/browser/manager.ts:16

// fallow-ignore-file code-duplication
import { execSync, spawnSync } from "node:child_process";
import { existsSync, mkdirSync, readdirSync, rmSync, statSync, utimesSync } from "node:fs";
import { basename } from "node:path";
import { homedir } from "node:os";
import { join } from "node:path";
import { normalizeErrorMessage } from "../utils/errorMessage.js";

type PuppeteerBrowsers = typeof import("@puppeteer/browsers");

async function loadPuppeteerBrowsers(): Promise<PuppeteerBrowsers> {
  try {
    return await import("@puppeteer/browsers");
  } catch (err) {
    const cause = normalizeErrorMessage(err);
    throw new Error(
      `Failed to load @puppeteer/browsers: ${cause}\n` +
        `Fix: run \`npm install\` or \`bun install\` to restore missing packages, then retry.`,
    );
  }
}

const CHROME_VERSION = "152.0.7928.2";
const CACHE_ROOT_DIR = join(homedir(), ".cache", "hyperframes");
const CACHE_DIR = join(homedir(), ".cache", "hyperframes", "chrome");
// Puppeteer's managed cache — where `@puppeteer/browsers install
// chrome-headless-shell` (and `puppeteer install`) drop binaries. The engine's
// `resolveHeadlessShellPath` scans the same directory; the CLI must look here
// too or it silently picks system Chrome over a perfectly good headless-shell.
const PUPPETEER_CACHE_DIR = join(homedir(), ".cache", "puppeteer", "chrome-headless-shell");

// `@puppeteer/browsers`' install() has no concurrency guard of its own — two
// CLI invocations that both miss the cache at the same time both extract into
// the same target directory simultaneously. A killed/interrupted extraction

View on GitHub (pinned to c2996c8626)

Solutions

  1. Run `bun install` (or `npm install`) at the repo root to restore missing packages, then retry.
  2. If using a monorepo, ensure @puppeteer/browsers is a real dependency of packages/cli (check packages/cli/package.json).
  3. Clear node_modules and reinstall if the install is partial: remove node_modules, reinstall.

Example fix

# before — missing package
$ hyperframes render ...
Error: Failed to load @puppeteer/browsers ...
# restore
$ bun install
$ hyperframes render ...
Defensive patterns

Strategy: validation

Validate before calling

async function puppeteerBrowsersResolvable(): Promise<boolean> {
  try { await import('@puppeteer/browsers'); return true; } catch { return false; }
}
if (!await puppeteerBrowsersResolvable()) {
  throw new Error('@puppeteer/browsers missing. Run `bun install`.');
}

Try / catch

try {
  await ensureBrowser();
} catch (err) {
  if (/Failed to load @puppeteer\/browsers/i.test((err as Error).message)) {
    console.error('Run `bun install` to restore packages, then retry.');
  }
  throw err;
}

Prevention

When it happens

Trigger: ensureBrowser()/downloadBrowser() calls loadPuppeteerBrowsers(); the `import('@puppeteer/browsers')` throws — package not installed, hoisted away in a monorepo where the CLI package can't resolve it, or node_modules is partially deleted.

Common situations: Fresh clone where `bun install` was interrupted; a production deploy that pruned devDependencies and @puppeteer/browsers was marked dev; pnpm-style hoisting incompatibility; manual deletion of part of node_modules.

Related errors


AI-assisted analysis of heygen-com/hyperframes@c2996c8626 (2026-08-12). Data as JSON: /api/errors/83e441c8fca2b6a8. Report an issue: GitHub.