{"record":{"id":"d6a0582d7df0e88f","repo":"heygen-com/hyperframes","slug":"build-zip-unzipped-bundle-formatbytes-unzipped","errorCode":null,"errorMessage":"[build-zip] unzipped bundle ${formatBytes(unzippedBytes)} exceeds limit ${formatBytes(opts.maxUnzippedBytes)} (Lambda ZIP ceiling: 250 MiB unzipped). Switch --source to the lighter option, or move Chrome to a Lambda Layer.","messagePattern":"\\[build-zip\\] unzipped bundle (.+?) exceeds limit (.+?) \\(Lambda ZIP ceiling: 250 MiB unzipped\\)\\. Switch --source to the lighter option, or move Chrome to a Lambda Layer\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/aws-lambda/scripts/build-zip.ts","lineNumber":148,"sourceCode":"  stageFfmpeg(stagingDir);\n\n  // 3b. Stage the hyperframe runtime manifest + IIFE as siblings of\n  //     handler.mjs. The producer's `hyperframeRuntimeLoader` checks\n  //     SIBLING_MANIFEST_PATH first, so dropping the manifest alongside\n  //     the bundled handler at /var/task/hyperframe.manifest.json lets\n  //     renderChunk find it without needing PRODUCER_HYPERFRAME_MANIFEST_PATH.\n  stageHyperframeRuntime(stagingDir);\n\n  // 4. If we're on the chrome-headless-shell fallback, stage that binary.\n  if (opts.source === \"chrome-headless-shell\") {\n    stageChromeHeadlessShell(stagingDir);\n  }\n\n  // 5. Compute the unzipped size BEFORE zipping so we fail loud when over budget.\n  const unzippedBytes = directorySizeBytes(stagingDir);\n  console.log(`[build-zip] unzipped staging size: ${formatBytes(unzippedBytes)}`);\n  if (unzippedBytes > opts.maxUnzippedBytes) {\n    throw new Error(\n      `[build-zip] unzipped bundle ${formatBytes(unzippedBytes)} exceeds limit ${formatBytes(\n        opts.maxUnzippedBytes,\n      )} (Lambda ZIP ceiling: 250 MiB unzipped). ` +\n        `Switch --source to the lighter option, or move Chrome to a Lambda Layer.`,\n    );\n  }\n\n  // 6. Build the ZIP.\n  const zipPath = join(distDir, \"handler.zip\");\n  zipDirectory(stagingDir, zipPath);\n  const zippedBytes = statSync(zipPath).size;\n  console.log(`[build-zip] zip size: ${formatBytes(zippedBytes)} → ${zipPath}`);\n  if (zippedBytes > opts.maxZippedBytes) {\n    throw new Error(\n      `[build-zip] zip ${formatBytes(zippedBytes)} exceeds ZIP size limit ${formatBytes(\n        opts.maxZippedBytes,\n      )}.`,\n    );","sourceCodeStart":130,"sourceCodeEnd":166,"githubUrl":"https://github.com/heygen-com/hyperframes/blob/c2996c8626135db5253519359d8a063d3bafad8d/packages/aws-lambda/scripts/build-zip.ts#L130-L166","documentation":"AWS Lambda enforces a 250 MiB unzipped deployment package ceiling. The build computes the staging directory size via 'du -sb' before zipping and throws if it exceeds opts.maxUnzippedBytes (default 250 MiB). This prevents shipping a bundle that Lambda will reject at deploy time with a less clear error.","triggerScenarios":"The staged directory (node_modules + ffmpeg + ffprobe + hyperframe runtime + optional Chrome binary) exceeds the unzipped byte limit. Most common when using --source=chrome-headless-shell, which adds ~150+ MiB of Chrome on top of ffmpeg and node_modules.","commonSituations":"Building with chrome-headless-shell source on a host where Chromium is large; dependency bloat from newly added packages; using the heavier source when sparticuz would suffice.","solutions":["Switch to --source=sparticuz (the lighter Chromium provider).","Move Chrome and/or ffmpeg to a Lambda Layer instead of bundling in the ZIP.","Raise the limit with --max-unzipped=<bytes> only if you are confident the Lambda unzipped ceiling does not apply (e.g., layers)."],"exampleFix":"// before\ntsx build-zip.ts --source=chrome-headless-shell\n\n// after\ntsx build-zip.ts --source=sparticuz","handlingStrategy":"validation","validationCode":"import { spawnSync } from \"node:child_process\";\nconst stagingSize = parseInt(\n  spawnSync(\"du\", [\"-sb\", stagingDir], { encoding: \"utf-8\" }).stdout.split(/\\s+/)[0] ?? \"0\",\n  10,\n);\nconst LIMIT = 250 * 1024 * 1024;\nif (stagingSize > LIMIT) {\n  console.error(`Unzipped size ${stagingSize} exceeds ${LIMIT}. Switch to --source=sparticuz or use a Lambda Layer.`);\n  process.exit(1);\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Default to --source=sparticuz to stay under the unzipped ceiling.","Move Chrome and ffmpeg to a Lambda Layer for production deployments.","Monitor staging size in CI as a regression signal."],"tags":["aws-lambda","size-limit","build","deployment"],"backgroundTag":null,"analyzedSha":"c2996c8626135db5253519359d8a063d3bafad8d","analyzedAt":"2026-08-12T22:18:56.877Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}