{"record":{"id":"e1242c1d8e0e7a8c","repo":"heygen-com/hyperframes","slug":"build-zip-chrome-headless-shell-cache-missing-at","errorCode":null,"errorMessage":"[build-zip] chrome-headless-shell cache missing at ${baseDir}. Run\n  npx --yes @puppeteer/browsers install chrome-headless-shell@stable --path ${home}/.cache/puppeteer\nbefore --source=chrome-headless-shell.","messagePattern":"\\[build-zip\\] chrome-headless-shell cache missing at (.+?)\\. Run\n  npx --yes @puppeteer/browsers install chrome-headless-shell@stable --path (.+?)/\\.cache/puppeteer\nbefore --source=chrome-headless-shell\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/aws-lambda/scripts/build-zip.ts","lineNumber":448,"sourceCode":"      `[build-zip] ffprobe-static binary not found under ${ffprobeModule}/bin/linux/. Did postinstall run?`,\n    );\n  }\n  const ffprobeDest = join(binDir, \"ffprobe\");\n  cpSync(ffprobeBinary, ffprobeDest);\n  chmodSync(ffprobeDest, 0o755);\n\n  console.log(`[build-zip] staged ffmpeg + ffprobe → bin/`);\n}\n\nfunction stageChromeHeadlessShell(stagingDir: string): void {\n  // The fallback path bundles the same chrome-headless-shell binary the\n  // K8s deploy uses. The binary is fetched via `@puppeteer/browsers` on\n  // first build into the host's `~/.cache/puppeteer/`; the build script\n  // re-uses that cache rather than redownloading.\n  const home = process.env.HOME ?? \"/root\";\n  const baseDir = join(home, \".cache\", \"puppeteer\", \"chrome-headless-shell\");\n  if (!existsSync(baseDir)) {\n    throw new Error(\n      `[build-zip] chrome-headless-shell cache missing at ${baseDir}. Run\\n` +\n        `  npx --yes @puppeteer/browsers install chrome-headless-shell@stable --path ${home}/.cache/puppeteer\\n` +\n        `before --source=chrome-headless-shell.`,\n    );\n  }\n  // Sort by numeric semver descending. `sort().reverse()` is lexicographic,\n  // which silently picks \"99.0.0\" over \"131.0.0\" once Chrome ships\n  // three-digit majors that aren't strictly width-aligned. `compareSemver`\n  // returns negative/zero/positive on (a, b), so descending = `b - a`.\n  const versions = readdirSync(baseDir).sort((a, b) => compareSemver(b, a));\n  for (const v of versions) {\n    const candidate = join(baseDir, v, \"chrome-headless-shell-linux64\", \"chrome-headless-shell\");\n    if (existsSync(candidate)) {\n      const dest = join(stagingDir, \"bin\", \"chrome-headless-shell\");\n      mkdirSync(dirname(dest), { recursive: true });\n      cpSync(candidate, dest);\n      chmodSync(dest, 0o755);\n      console.log(`[build-zip] staged chrome-headless-shell (${v}) → bin/chrome-headless-shell`);","sourceCodeStart":430,"sourceCodeEnd":466,"githubUrl":"https://github.com/heygen-com/hyperframes/blob/c2996c8626135db5253519359d8a063d3bafad8d/packages/aws-lambda/scripts/build-zip.ts#L430-L466","documentation":"When using --source=chrome-headless-shell, the build expects Puppeteer's browser cache at ~/.cache/puppeteer/chrome-headless-shell/. If that directory does not exist, it throws with the exact npx @puppeteer/browsers install command to populate the cache. The build reuses the cache rather than downloading Chrome itself.","triggerScenarios":"Running with --source=chrome-headless-shell without having first run the Puppeteer browsers install command. The cache directory is absent.","commonSituations":"First build with chrome-headless-shell source on a new machine; CI environment without the Puppeteer cache; HOME environment variable pointing somewhere unexpected so the cache path resolves wrong.","solutions":["Run: npx --yes @puppeteer/browsers install chrome-headless-shell@stable --path ~/.cache/puppeteer","Switch to --source=sparticuz which does not need the Puppeteer cache.","In CI, add the install command as a pre-build step and cache the ~/.cache/puppeteer directory."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"import { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nconst home = process.env.HOME ?? \"/root\";\nconst cacheDir = join(home, \".cache\", \"puppeteer\", \"chrome-headless-shell\");\nif (!existsSync(cacheDir)) {\n  console.error(`Puppeteer cache missing at ${cacheDir}. Run: npx --yes @puppeteer/browsers install chrome-headless-shell@stable --path ${join(home, \".cache\", \"puppeteer\")}`);\n  process.exit(1);\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Pre-install chrome-headless-shell in CI before running build-zip.","Cache ~/.cache/puppeteer across CI runs to avoid re-downloading.","Default to --source=sparticuz to avoid the Puppeteer cache dependency."],"tags":["chrome-headless-shell","puppeteer","cache","build"],"backgroundTag":null,"analyzedSha":"c2996c8626135db5253519359d8a063d3bafad8d","analyzedAt":"2026-08-12T22:18:56.877Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}