Describe the bug
handlePlan in @hyperframes/aws-lambda never sets PRODUCER_HEADLESS_SHELL_PATH, so the moment the producer's probe stage tries to launch Chromium inside Lambda the invocation fails with:
Error: An `executablePath` or `channel` must be specified for `puppeteer-core`
at assert (/var/task/node_modules/puppeteer-core/src/util/assert.ts:19:11)
at ChromeLauncher.computeLaunchArguments (/var/task/node_modules/puppeteer-core/src/node/ChromeLauncher.ts:125:7)
at async ChromeLauncher.launch (/var/task/node_modules/puppeteer-core/src/node/BrowserLauncher.ts:119:24)
at async launchBrowser (file:///var/task/handler.mjs:376:17)
at async acquireBrowser (file:///var/task/handler.mjs:349:22)
at async createCaptureSession (file:///var/task/handler.mjs:12223:36)
at async runProbeStage (file:///var/task/handler.mjs:36445:20)
at async plan (file:///var/task/handler.mjs:37797:23)
at async handlePlan (file:///var/task/handler.mjs:41843:20)
handleRenderChunk resolves Chromium at the top of the function (packages/aws-lambda/src/handler.ts:299-309), but handlePlan skips that step entirely. Anything that makes runProbeStage create a browser session — currently root duration unknown, unresolved nested compositions, or data-hf-auto-start media — can therefore abort before the plan step finishes writing plan.tar.gz.
Note: inline requestAnimationFrame(...) routes capture through screenshot mode later in the pipeline, but it does not by itself force the Plan-stage browser probe unless one of the probe conditions above is also present.
Link to reproduction
Hit this in production while wiring HyperFrames into a customer pipeline; happy to put a stripped-down public repo together if it would help triage. The minimum case is any composition that ends up in runProbeStage with no static duration and no pre-seeded PRODUCER_HEADLESS_SHELL_PATH.
Steps to reproduce
- Author a composition where
runProbeStage is exercised. The fast way is to omit a static root data-duration so the producer has to probe duration from the browser.
hyperframes lambda deploy (or use an existing stack on @hyperframes/aws-lambda@0.6.40).
hyperframes lambda render <project> --wait.
Expected behavior
Plan resolves Chromium the same way render-chunk does, the probe stage launches headless Chrome, and the plan tar lands in S3.
Actual behavior
The plan Lambda fails with the puppeteer-core "executablePath" assertion. Step Functions retries the Plan task according to the state machine retry policy, then the render is reported as failed.
Root cause
packages/aws-lambda/src/handler.ts:227 — handlePlan body never touches PRODUCER_HEADLESS_SHELL_PATH. Compare with handleRenderChunk at line 299:
if (!deps?.skipChromeResolution && !process.env.PRODUCER_HEADLESS_SHELL_PATH) {
const chromePath = await resolveChromeExecutablePath();
process.env.PRODUCER_HEADLESS_SHELL_PATH = chromePath;
}
On a cold container the env var is unset, so plan launches puppeteer with executablePath: undefined. This can only be masked if a warm Lambda execution environment previously handled a renderChunk invocation from another execution and left the env var sticky; within a single Step Functions execution, Plan still runs before RenderChunks.
handleAssemble doesn't launch Chromium so it doesn't need the guard.
Environment
@hyperframes/aws-lambda@0.6.40 (also reproduces on 0.6.37–0.6.39)
- Lambda runtime
nodejs22.x, x86_64, Sparticuz Chromium
- AWS region
us-east-1
Notes
I have a candidate patch — same env-var guard copy-pasted into handlePlan, plus a dispatch test that pre-seeds the path and asserts plan doesn't overwrite it. Happy to send it as a PR if the surgical fix is the direction you'd take here. Alternative would be lifting the guard into the dispatcher so any future action gets it for free, but that'd also pre-warm Chromium for assemble which doesn't need it.
Describe the bug
handlePlanin@hyperframes/aws-lambdanever setsPRODUCER_HEADLESS_SHELL_PATH, so the moment the producer's probe stage tries to launch Chromium inside Lambda the invocation fails with:handleRenderChunkresolves Chromium at the top of the function (packages/aws-lambda/src/handler.ts:299-309), buthandlePlanskips that step entirely. Anything that makesrunProbeStagecreate a browser session — currently root duration unknown, unresolved nested compositions, ordata-hf-auto-startmedia — can therefore abort before the plan step finishes writingplan.tar.gz.Note: inline
requestAnimationFrame(...)routes capture through screenshot mode later in the pipeline, but it does not by itself force the Plan-stage browser probe unless one of the probe conditions above is also present.Link to reproduction
Hit this in production while wiring HyperFrames into a customer pipeline; happy to put a stripped-down public repo together if it would help triage. The minimum case is any composition that ends up in
runProbeStagewith no static duration and no pre-seededPRODUCER_HEADLESS_SHELL_PATH.Steps to reproduce
runProbeStageis exercised. The fast way is to omit a static rootdata-durationso the producer has to probe duration from the browser.hyperframes lambda deploy(or use an existing stack on@hyperframes/aws-lambda@0.6.40).hyperframes lambda render <project> --wait.Expected behavior
Plan resolves Chromium the same way render-chunk does, the probe stage launches headless Chrome, and the plan tar lands in S3.
Actual behavior
The plan Lambda fails with the puppeteer-core "executablePath" assertion. Step Functions retries the Plan task according to the state machine retry policy, then the render is reported as failed.
Root cause
packages/aws-lambda/src/handler.ts:227—handlePlanbody never touchesPRODUCER_HEADLESS_SHELL_PATH. Compare withhandleRenderChunkat line 299:On a cold container the env var is unset, so plan launches puppeteer with
executablePath: undefined. This can only be masked if a warm Lambda execution environment previously handled arenderChunkinvocation from another execution and left the env var sticky; within a single Step Functions execution, Plan still runs before RenderChunks.handleAssembledoesn't launch Chromium so it doesn't need the guard.Environment
@hyperframes/aws-lambda@0.6.40(also reproduces on0.6.37–0.6.39)nodejs22.x, x86_64, Sparticuz Chromiumus-east-1Notes
I have a candidate patch — same env-var guard copy-pasted into
handlePlan, plus a dispatch test that pre-seeds the path and asserts plan doesn't overwrite it. Happy to send it as a PR if the surgical fix is the direction you'd take here. Alternative would be lifting the guard into the dispatcher so any future action gets it for free, but that'd also pre-warm Chromium forassemblewhich doesn't need it.