heygen-com/hyperframes · error · Error

[build-zip] no linux64 chrome-headless-shell binary found un

Error message

[build-zip] no linux64 chrome-headless-shell binary found under ${baseDir}.

What it means

The Puppeteer cache directory exists, but the build iterates cached version directories (sorted by semver descending) looking for <version>/chrome-headless-shell-linux64/chrome-headless-shell. If no version directory contains the linux64 binary, it throws.

Source

Thrown at packages/aws-lambda/scripts/build-zip.ts:470

    );
  }
  // Sort by numeric semver descending. `sort().reverse()` is lexicographic,
  // which silently picks "99.0.0" over "131.0.0" once Chrome ships
  // three-digit majors that aren't strictly width-aligned. `compareSemver`
  // returns negative/zero/positive on (a, b), so descending = `b - a`.
  const versions = readdirSync(baseDir).sort((a, b) => compareSemver(b, a));
  for (const v of versions) {
    const candidate = join(baseDir, v, "chrome-headless-shell-linux64", "chrome-headless-shell");
    if (existsSync(candidate)) {
      const dest = join(stagingDir, "bin", "chrome-headless-shell");
      mkdirSync(dirname(dest), { recursive: true });
      cpSync(candidate, dest);
      chmodSync(dest, 0o755);
      console.log(`[build-zip] staged chrome-headless-shell (${v}) → bin/chrome-headless-shell`);
      return;
    }
  }
  throw new Error(`[build-zip] no linux64 chrome-headless-shell binary found under ${baseDir}.`);
}

/**
 * Compare two semver-shaped strings like "131.0.6778.108". Treats any
 * non-numeric directory name as `-Infinity` so it sorts to the bottom
 * (Puppeteer's cache layout sometimes includes `latest` or branch tags).
 * Used by `stageChromeHeadlessShell` to pick the newest cached Chrome
 * without tripping on the lexicographic "99 > 131" trap.
 */
function compareSemver(a: string, b: string): number {
  const partsA = a.split(".").map((s) => Number.parseInt(s, 10));
  const partsB = b.split(".").map((s) => Number.parseInt(s, 10));
  const len = Math.max(partsA.length, partsB.length);
  for (let i = 0; i < len; i++) {
    const ai = partsA[i] ?? 0;
    const bi = partsB[i] ?? 0;
    if (Number.isNaN(ai) && Number.isNaN(bi)) continue;
    if (Number.isNaN(ai)) return -1;

View on GitHub (pinned to c2996c8626)

Solutions

  1. Install the linux64 variant: npx --yes @puppeteer/browsers install chrome-headless-shell@stable --platform=linux --path ~/.cache/puppeteer
  2. Build inside a linux/amd64 Docker container so the cache is populated with linux binaries.
  3. Switch to --source=sparticuz to avoid the chrome-headless-shell dependency entirely.
Defensive patterns

Strategy: validation

Validate before calling

import { existsSync, readdirSync } from "node:fs";
import { join } from "node:path";
const home = process.env.HOME ?? "/root";
const baseDir = join(home, ".cache", "puppeteer", "chrome-headless-shell");
const hasLinux64 = readdirSync(baseDir).some((v) =>
  existsSync(join(baseDir, v, "chrome-headless-shell-linux64", "chrome-headless-shell")),
);
if (!hasLinux64) {
  console.error("No linux64 chrome-headless-shell cached. Install with --platform=linux.");
  process.exit(1);
}

Prevention

When it happens

Trigger: The Puppeteer cache has Chrome versions but none have the chrome-headless-shell-linux64 subdirectory — only macOS or Windows variants were cached, or the cache has full Chrome but not chrome-headless-shell.

Common situations: Building on macOS where @puppeteer/browsers install fetched chrome-headless-shell-mac-64 or chrome-headless-shell-mac-arm64; cache was populated on a non-Linux host.

Related errors


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