From 6a1c596462f496d5ae9619a0de4801841bfc508f Mon Sep 17 00:00:00 2001 From: "Abdullah M. Nazif" Date: Fri, 9 Oct 2026 03:52:19 +0800 Subject: [PATCH 1/6] docs(aws-lambda): explain long-video planning limits --- docs/deploy/aws-lambda.mdx | 25 +++++++++++++++++++++---- docs/packages/aws-lambda.mdx | 4 +++- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/docs/deploy/aws-lambda.mdx b/docs/deploy/aws-lambda.mdx index 3a35e805cfc..4c8d7af57ba 100644 --- a/docs/deploy/aws-lambda.mdx +++ b/docs/deploy/aws-lambda.mdx @@ -210,6 +210,20 @@ Most often a Lambda cold-start chain on a many-chunk render. The Map state's res If progress doesn't advance for >10 minutes, check the Step Functions execution in the AWS console — failed Lambda invocations include the typed error name (`FONT_FETCH_FAILED`, `FFMPEG_VERSION_MISMATCH`, etc.) which short-circuits the state machine. +### Long-video planning times out + +A Plan failure with `VIDEO_EXTRACTION_FAILED` and `ffmpeg_timeout` means an FFmpeg process exceeded its deadline. Plan extracts video frames before Step Functions starts the chunk workers. Constant-frame-rate ranges longer than two minutes are extracted in two-minute segments; the default 300,000 ms timeout applies to each segment. Variable-frame-rate sources are not divided into these segments, so extraction of a long range can still exceed that deadline. + +For a variable-frame-rate source whose extraction fits within the Lambda invocation budget, increase `FFMPEG_PROCESS_TIMEOUT_MS` on the deployed render function. Setting it only in the terminal running `lambda render` does not configure the remote function. CDK users can add the environment variable before deploying their stack: + +```ts +render.renderFunction.addEnvironment("FFMPEG_PROCESS_TIMEOUT_MS", "600000"); +``` + +This gives each FFmpeg process ten minutes. The entire Plan invocation still has to finish within the function's configured timeout, at most 15 minutes, including project download, probing, audio preparation, and artifact uploads. Check the Plan invocation's CloudWatch logs to measure that budget. Reducing `chunkSize` or increasing chunk-worker concurrency does not divide Plan's extraction work. + +If planning cannot fit, split the composition into shorter, frame-aligned projects, render each as a separate job, and combine the results. Choose part lengths from measured planning times. There is no fixed maximum video length: resolution, frame rate, codec, and Lambda resources affect extraction time. + ### Tearing down doesn't reclaim S3 storage The render bucket is created with CloudFormation `Retain` on delete — `hyperframes lambda destroy` (or `sam delete`) tears the function + state machine down but the bucket survives. This is intentional: it protects final-rendered MP4s from being lost when you re-deploy. To fully reclaim storage, empty + delete the bucket via the AWS console / `aws s3 rb`. @@ -225,12 +239,15 @@ The render bucket is created with CloudFormation `Retain` on delete — `hyperfr - The distributed AWS path is SDR-only. - Video length is bounded by the plan step. One Lambda invocation extracts every frame of every video and uploads them before any chunk starts, and it - must finish inside Lambda's 15-minute limit. On a 10,240 MB function, a + must finish within the configured Lambda timeout, at most 15 minutes. + RenderChunk and Assemble each have their own invocation deadline; all three + phases must also fit the function's `/tmp` storage. On a 10,240 MB function, a 26-minute 576×1024 24 fps H.264 clip plans in about 3.5 minutes. Larger frames, high-bitrate footage and more videos take proportionally longer. - ffmpeg extracts long clips in 2-minute segments, so `FFMPEG_PROCESS_TIMEOUT_MS` - applies to each segment, not to the whole clip. Split edits that approach the - limit into separate renders. + FFmpeg extracts long constant-frame-rate ranges in two-minute segments, so + `FFMPEG_PROCESS_TIMEOUT_MS` applies to each segment. Variable-frame-rate + sources remain unsegmented. See [long-video planning timeouts](#long-video-planning-times-out) + for timeout configuration and splitting edits that approach the limit. ## Related topics diff --git a/docs/packages/aws-lambda.mdx b/docs/packages/aws-lambda.mdx index 199be356c56..92da63a861b 100644 --- a/docs/packages/aws-lambda.mdx +++ b/docs/packages/aws-lambda.mdx @@ -44,7 +44,7 @@ The Lambda adapter wraps the distributed producer primitives behind one dispatch - Downloads the project archive from S3, runs the producer planner, and uploads the plan directory. + Downloads the project archive from S3, probes the composition, extracts its video frames, prepares audio, and publishes the plan artifacts before chunk workers start. Step Functions fans out chunk jobs. Each Lambda invocation downloads the plan assets, renders one chunk, and uploads the result. @@ -54,6 +54,8 @@ The Lambda adapter wraps the distributed producer primitives behind one dispatch +All three phases must fit the configured per-invocation Lambda timeout, at most 15 minutes. Video extraction happens in Plan, so smaller chunks do not reduce this phase's work. See [long-video planning limits](/deploy/aws-lambda#long-video-planning-times-out) for per-segment and variable-frame-rate FFmpeg timeouts and how to split jobs that cannot fit. + The handler uses `@sparticuz/chromium` by default, with a build-time fallback for bundling `chrome-headless-shell` directly when needed. ## Using the SDK From 4eb3914fc5961df86c145bfc39a2aa682ccde3c6 Mon Sep 17 00:00:00 2001 From: "Abdullah M. Nazif" Date: Fri, 9 Oct 2026 19:03:53 +0800 Subject: [PATCH 2/6] docs(aws-lambda): prepare planning guidance for main update --- docs/deploy/aws-lambda.mdx | 27 ++++++++++++++------------- docs/packages/aws-lambda.mdx | 6 +++--- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/docs/deploy/aws-lambda.mdx b/docs/deploy/aws-lambda.mdx index 4c8d7af57ba..17af7172f71 100644 --- a/docs/deploy/aws-lambda.mdx +++ b/docs/deploy/aws-lambda.mdx @@ -212,7 +212,9 @@ If progress doesn't advance for >10 minutes, check the Step Functions execution ### Long-video planning times out -A Plan failure with `VIDEO_EXTRACTION_FAILED` and `ffmpeg_timeout` means an FFmpeg process exceeded its deadline. Plan extracts video frames before Step Functions starts the chunk workers. Constant-frame-rate ranges longer than two minutes are extracted in two-minute segments; the default 300,000 ms timeout applies to each segment. Variable-frame-rate sources are not divided into these segments, so extraction of a long range can still exceed that deadline. +A failure with `VIDEO_EXTRACTION_FAILED` and `ffmpeg_timeout` means an FFmpeg process exceeded its deadline. Check the failed invocation's CloudWatch logs to distinguish Plan from RenderChunk. Plan v2 leaves eligible constant-frame-rate SDR video extraction to the chunk workers after a short timing probe. Variable-frame-rate sources and required HDR color conversions still run during Plan; the deprecated v1 path also extracts videos during Plan. + +Constant-frame-rate extraction uses segments of up to two minutes, with the default 300,000 ms timeout applied to each segment. Variable-frame-rate extraction is not segmented, so a long range can exceed that deadline even when shorter ranges succeed. For a variable-frame-rate source whose extraction fits within the Lambda invocation budget, increase `FFMPEG_PROCESS_TIMEOUT_MS` on the deployed render function. Setting it only in the terminal running `lambda render` does not configure the remote function. CDK users can add the environment variable before deploying their stack: @@ -220,7 +222,7 @@ For a variable-frame-rate source whose extraction fits within the Lambda invocat render.renderFunction.addEnvironment("FFMPEG_PROCESS_TIMEOUT_MS", "600000"); ``` -This gives each FFmpeg process ten minutes. The entire Plan invocation still has to finish within the function's configured timeout, at most 15 minutes, including project download, probing, audio preparation, and artifact uploads. Check the Plan invocation's CloudWatch logs to measure that budget. Reducing `chunkSize` or increasing chunk-worker concurrency does not divide Plan's extraction work. +This gives each FFmpeg process ten minutes. Plan, RenderChunk, and Assemble each have to finish within the function's configured timeout, at most 15 minutes, including downloads, processing, and uploads, and fit its `/tmp` storage. Measure the failing phase's time and storage use in CloudWatch. Smaller chunks or a higher `maxParallelChunks` can reduce each chunk's work; they do not divide video extraction that still happens during Plan. If planning cannot fit, split the composition into shorter, frame-aligned projects, render each as a separate job, and combine the results. Choose part lengths from measured planning times. There is no fixed maximum video length: resolution, frame rate, codec, and Lambda resources affect extraction time. @@ -237,17 +239,16 @@ The render bucket is created with CloudFormation `Retain` on delete — `hyperfr - Each `--region` is an independent stack. Cross-region failover is not built in. - The distributed AWS path is SDR-only. -- Video length is bounded by the plan step. One Lambda invocation extracts - every frame of every video and uploads them before any chunk starts, and it - must finish within the configured Lambda timeout, at most 15 minutes. - RenderChunk and Assemble each have their own invocation deadline; all three - phases must also fit the function's `/tmp` storage. On a 10,240 MB function, a - 26-minute 576×1024 24 fps H.264 clip plans in about 3.5 minutes. Larger - frames, high-bitrate footage and more videos take proportionally longer. - FFmpeg extracts long constant-frame-rate ranges in two-minute segments, so - `FFMPEG_PROCESS_TIMEOUT_MS` applies to each segment. Variable-frame-rate - sources remain unsegmented. See [long-video planning timeouts](#long-video-planning-times-out) - for timeout configuration and splitting edits that approach the limit. +- Each render chunk extracts only the video frames it shows, so the plan step + no longer grows with video length. On a 10,240 MB function, a 26-minute + 576×1024 24 fps H.264 clip plans in about 20 seconds. Each chunk downloads + the whole source file of every video it shows. Variable-frame-rate and HDR + sources are the exception: the plan step still extracts every frame of those, + inside Lambda's 15-minute limit, in 2-minute segments, so + `FFMPEG_PROCESS_TIMEOUT_MS` applies to each segment, not to the whole clip. +- A chunk must also finish inside 15 minutes. For long renders, raise + `--max-parallel-chunks` (`maxParallelChunks` in the SDK) so each chunk covers + fewer frames. ## Related topics diff --git a/docs/packages/aws-lambda.mdx b/docs/packages/aws-lambda.mdx index 92da63a861b..220d11bb598 100644 --- a/docs/packages/aws-lambda.mdx +++ b/docs/packages/aws-lambda.mdx @@ -44,17 +44,17 @@ The Lambda adapter wraps the distributed producer primitives behind one dispatch - Downloads the project archive from S3, probes the composition, extracts its video frames, prepares audio, and publishes the plan artifacts before chunk workers start. + Downloads the project archive from S3, probes the composition, prepares audio, and publishes the plan artifacts. Plan v2 defers eligible constant-frame-rate SDR video extraction to the chunk workers; variable-frame-rate sources and required HDR color conversions still run during planning. - Step Functions fans out chunk jobs. Each Lambda invocation downloads the plan assets, renders one chunk, and uploads the result. + Step Functions fans out chunk jobs. Each Lambda invocation downloads the assets it needs, extracts its deferred video frames, renders one chunk, and uploads the result. Downloads all rendered chunks plus audio assets, assembles the final output, and writes the finished file to S3. -All three phases must fit the configured per-invocation Lambda timeout, at most 15 minutes. Video extraction happens in Plan, so smaller chunks do not reduce this phase's work. See [long-video planning limits](/deploy/aws-lambda#long-video-planning-times-out) for per-segment and variable-frame-rate FFmpeg timeouts and how to split jobs that cannot fit. +All three phases must fit the configured per-invocation Lambda timeout, at most 15 minutes, and the function's temporary storage. Smaller chunks can reduce each worker's extraction and rendering work, but do not divide video extraction that still happens in Plan. See [long-video planning limits](/deploy/aws-lambda#long-video-planning-times-out) for per-segment and variable-frame-rate FFmpeg timeouts and how to split jobs that cannot fit. The handler uses `@sparticuz/chromium` by default, with a build-time fallback for bundling `chrome-headless-shell` directly when needed. From 407613bdefd36651beec3096d4f673548f2d79cf Mon Sep 17 00:00:00 2001 From: "Abdullah M. Nazif" Date: Fri, 9 Oct 2026 19:03:59 +0800 Subject: [PATCH 3/6] docs(aws-lambda): align planning limits with chunk extraction --- docs/deploy/aws-lambda.mdx | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/docs/deploy/aws-lambda.mdx b/docs/deploy/aws-lambda.mdx index 17af7172f71..2a8f61489d5 100644 --- a/docs/deploy/aws-lambda.mdx +++ b/docs/deploy/aws-lambda.mdx @@ -239,15 +239,17 @@ The render bucket is created with CloudFormation `Retain` on delete — `hyperfr - Each `--region` is an independent stack. Cross-region failover is not built in. - The distributed AWS path is SDR-only. -- Each render chunk extracts only the video frames it shows, so the plan step - no longer grows with video length. On a 10,240 MB function, a 26-minute - 576×1024 24 fps H.264 clip plans in about 20 seconds. Each chunk downloads - the whole source file of every video it shows. Variable-frame-rate and HDR - sources are the exception: the plan step still extracts every frame of those, - inside Lambda's 15-minute limit, in 2-minute segments, so - `FFMPEG_PROCESS_TIMEOUT_MS` applies to each segment, not to the whole clip. -- A chunk must also finish inside 15 minutes. For long renders, raise - `--max-parallel-chunks` (`maxParallelChunks` in the SDK) so each chunk covers +- Plan v2 leaves eligible constant-frame-rate SDR video extraction to each + render chunk, which extracts only the frames it shows. On a 10,240 MB + function, a 26-minute 576×1024 24 fps H.264 clip plans in about 20 seconds. Each chunk downloads + the whole source file of every video it shows. Variable-frame-rate sources + and required HDR color conversions still need extraction during Plan. + Constant-frame-rate extraction uses two-minute segments; variable-frame-rate + extraction remains unsegmented. See [long-video planning timeouts](#long-video-planning-times-out) + for timeout configuration and splitting jobs that cannot fit. +- Plan, each RenderChunk, and Assemble must finish within their own configured + Lambda timeout, at most 15 minutes, and fit the function's `/tmp` storage. + For long renders, raise `--max-parallel-chunks` (`maxParallelChunks` in the SDK) so each chunk covers fewer frames. ## Related topics From c0f0e8c8d5db6f76f4d9230bb7370d8c5743150e Mon Sep 17 00:00:00 2001 From: "Abdullah M. Nazif" Date: Sat, 24 Oct 2026 14:12:40 +0800 Subject: [PATCH 4/6] chore: prepare conflict resolution against current main --- docs/deploy/aws-lambda.mdx | 83 ++++++++++++++++++++++---------------- 1 file changed, 48 insertions(+), 35 deletions(-) diff --git a/docs/deploy/aws-lambda.mdx b/docs/deploy/aws-lambda.mdx index 2a8f61489d5..d85dfa153b3 100644 --- a/docs/deploy/aws-lambda.mdx +++ b/docs/deploy/aws-lambda.mdx @@ -46,9 +46,8 @@ The Lambda handler is a thin dispatch: parse the Step Functions event, download |------|-----|---------| | AWS credentials | The CLI and the deploy step both call AWS APIs. | Env vars, `~/.aws/credentials`, SSO, or IMDS — any chain the AWS SDK for JavaScript v3 would resolve. | | AWS SAM CLI | `hyperframes lambda deploy/destroy` shells out to `sam deploy`/`sam delete`. | [Install guide](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/install-sam-cli.html) | -| `bun` | Used to build `packages/aws-lambda/dist/handler.zip` at deploy time. | `npm install -g bun` or [bun.sh](https://bun.sh) | -| Lambda adapter | The published CLI loads the AWS adapter only for Lambda commands. | `npm install -g @hyperframes/aws-lambda` alongside the CLI | -| HyperFrames repo checkout | `lambda deploy` builds the Lambda handler ZIP from source. Adopters who deploy outside a checkout can set `HYPERFRAMES_REPO_ROOT` to point at one. | `git clone https://github.com/heygen-com/hyperframes` | +| Lambda adapter | The published CLI loads the AWS adapter only for Lambda commands. Each release pins a prebuilt Lambda handler ZIP (a GitHub release asset); `lambda deploy` downloads it, checks its SHA-256 and deploys it with the adapter's SAM template. No repo checkout needed. | `npm install -g @hyperframes/aws-lambda` alongside the CLI, at the same version | +| HyperFrames repo checkout (optional) | Inside a checkout, or with `HYPERFRAMES_REPO_ROOT` pointing at one, `lambda deploy` builds the handler ZIP from source instead (needs `bun`). Use it to deploy unreleased changes, `--chrome-source=chrome-headless-shell`, or an adapter release older than the prebuilt handler. A checkout deploy on arm64 also needs the video image built from that checkout (`HYPERFRAMES_LAMBDA_VIDEO_IMAGE`), or `--video-arch x86_64`. | `git clone https://github.com/heygen-com/hyperframes`, and `npm install -g bun` or [bun.sh](https://bun.sh) | ## Three deployment paths @@ -60,14 +59,22 @@ The CLI is a thin wrapper around the SAM template + the `@hyperframes/aws-lambda hyperframes lambda deploy \ --stack-name=hyperframes-prod \ --region=us-east-1 \ - --concurrency=8 \ --memory=10240 ``` -The default `--concurrency=8` is deliberately conservative for first-time -users. It limits how many Lambda workers the deployed stack can run at once. -Raise it only after you have measured a typical render and checked the account's -limits and budget. +By default the stack reserves no concurrency. Each render reads the account's +unreserved Lambda concurrency when it plans, and runs up to 90% of it at once +(at most 256 chunks), leaving the rest for other functions in the account. The +plan logs the number it chose. Pass `--concurrency=N` to reserve `N` executions +for the function instead; every render then runs at most `N` chunks at once. + +Upgrading: redeploying an existing stack without `--concurrency` keeps the +reservation it already has, and `deploy` prints it. Stacks deployed by earlier +CLI versions reserved 8 by default, so they stay at 8 until you pass +`--concurrency=N`. Pass `--concurrency=none` to remove the reservation so each +render sizes itself to the account. + +`deploy` records the stack's bucket and state machine in `.hyperframes/lambda-stack-.json` under the current directory. Every other `lambda` command reads that file, so run them from the same directory with the same `--stack-name`, or they stop with "no stack state". Redeploying to upgrade rewrites the same file. After `deploy`, render anything with: @@ -81,7 +88,7 @@ See the [CLI reference](/packages/cli#hyperframes-lambda) for full flag document #### Pre-staging a project with `sites create` -Re-rendering the same project tree on every `lambda render` call re-tars and re-uploads it each time. For tight inner loops (CI smoke jobs, prompt iteration in a demo flow), pre-stage the project once and reuse the upload: +Every `lambda render` call without `--site-id` hashes the project tree and checks each file against the bucket. For tight inner loops (CI smoke jobs, prompt iteration in a demo flow), pre-stage the project once and reuse the upload: ```bash hyperframes lambda sites create ./my-project @@ -91,7 +98,19 @@ hyperframes lambda render ./my-project --site-id=a1b2c3d4e5f6g7h8 \ --width 1920 --height 1080 --wait ``` -The `siteId` is content-addressed via a SHA-256 of the project tree; re-running `sites create` on an unchanged tree skips the upload via a `HeadObject` short-circuit. Pass the same `--site-id` to as many `lambda render` calls as you like — they all reuse the one S3 PUT. +The `siteId` is content-addressed via a SHA-256 of the project tree. A site is a small `sites//files.json` that lists each file's path and SHA-256. The files themselves live once per bucket under `cas/sha256/`, shared by every site and render, so a file another project already uploaded is never uploaded again. Pass the same `--site-id` to as many `lambda render` calls as you like. + +The stack's bucket expires objects under `cas/sha256/` 30 days after they were last written (the `ExpireSharedContent` lifecycle rule). Each render or `sites create` that reuses a file at least 7 days old rewrites it in place, so files in use never expire. If a site's files did expire, `lambda render --site-id` re-uploads them from the project directory, if its copies still match. Otherwise, or when `renderToLambda` gets a `siteHandle` but no `projectDir`, the render stops with `SITE_FILES_MISSING` before starting; run `sites create` again to restore them. + +#### Upgrading from a version that deployed x86 only + +`lambda deploy` now runs the plan and the render chunks on an arm64 container-image function by default; audio and assemble stay on the x86 function. Redeploying an existing stack without `--video-arch` moves it to arm64 and adds the `-video` ECR repository. To keep a stack on x86, pass `--video-arch x86_64` on every deploy. `--chrome-source chrome-headless-shell` also needs `--video-arch x86_64`, because the arm64 image bundles sparticuz Chrome only. + +#### Upgrading from a version before shared content storage + +- **Redeploy the stack before upgrading the CLI or SDK that starts renders.** A stack from an earlier version cannot read `files.json` sites. The CLI and `renderToLambda` check the state machine first and stop with `STACK_TOO_OLD` before uploading anything; run `hyperframes lambda deploy` (or redeploy your SAM template or CDK construct from the same version) and render again. +- **Existing site ids keep working.** When `sites//files.json` is missing, the render uses that site's `project.tar.gz` from the earlier version. If neither exists the render stops with `SITE_NOT_FOUND`, naming both keys; run `sites create` again. +- **Every project uploads once more.** The site id scheme changed (it now also covers symlinked files), so `sites create` or the first `lambda render` of each project after the upgrade returns a new site id and uploads its files once. ### Path 2 — Direct SAM deploy @@ -117,7 +136,7 @@ The template emits three CloudFormation outputs you'll need to invoke renders: - `RenderFunctionArn` — the single Lambda function the state machine dispatches against. -The SAM template's own default for `ReservedConcurrency` is `-1` (unreserved, account-default). The Path 1 CLI overrides it to `8` to keep first-time spend bounded; if you drop `ReservedConcurrency` from `--parameter-overrides` here, you get the unreserved default. Set it explicitly unless you've already sized your typical render's fan-out. +The SAM template's default for `ReservedConcurrency` is `-1` (unreserved), the same as the Path 1 CLI: each render runs up to 90% of the account's unreserved concurrency. Set it to bound how many chunks run at once. ### Path 3 — CDK construct @@ -160,6 +179,8 @@ hyperframes lambda policies role hyperframes lambda policies validate ./infra/iam/hyperframes-deploy.json ``` +Code that calls the SDK's `renderToLambda` with its own role needs `states:DescribeStateMachine` on the state machine besides `states:StartExecution` and S3 read/write on the bucket: each render reads the definition to confirm the stack is new enough. + The generated documents grant `Resource: "*"` for the CLI's required action set. After your first successful deploy you can narrow `Resource` to the deployed ARNs — predictable per the CloudFormation outputs above. Adopters running the CLI in CI typically check the policy doc into source control and run `policies validate` as a pre-deploy step to catch drift. ## Cost shape @@ -210,25 +231,19 @@ Most often a Lambda cold-start chain on a many-chunk render. The Map state's res If progress doesn't advance for >10 minutes, check the Step Functions execution in the AWS console — failed Lambda invocations include the typed error name (`FONT_FETCH_FAILED`, `FFMPEG_VERSION_MISMATCH`, etc.) which short-circuits the state machine. -### Long-video planning times out - -A failure with `VIDEO_EXTRACTION_FAILED` and `ffmpeg_timeout` means an FFmpeg process exceeded its deadline. Check the failed invocation's CloudWatch logs to distinguish Plan from RenderChunk. Plan v2 leaves eligible constant-frame-rate SDR video extraction to the chunk workers after a short timing probe. Variable-frame-rate sources and required HDR color conversions still run during Plan; the deprecated v1 path also extracts videos during Plan. - -Constant-frame-rate extraction uses segments of up to two minutes, with the default 300,000 ms timeout applied to each segment. Variable-frame-rate extraction is not segmented, so a long range can exceed that deadline even when shorter ranges succeed. +### Tearing down doesn't reclaim S3 storage -For a variable-frame-rate source whose extraction fits within the Lambda invocation budget, increase `FFMPEG_PROCESS_TIMEOUT_MS` on the deployed render function. Setting it only in the terminal running `lambda render` does not configure the remote function. CDK users can add the environment variable before deploying their stack: +The render bucket is created with CloudFormation `Retain` on delete — `hyperframes lambda destroy` (or `sam delete`) tears the function + state machine down but the bucket survives. This is intentional: it protects final-rendered MP4s from being lost when you re-deploy. To fully reclaim storage, empty + delete the bucket via the AWS console / `aws s3 rb`. -```ts -render.renderFunction.addEnvironment("FFMPEG_PROCESS_TIMEOUT_MS", "600000"); -``` +A stack deployed on arm64 (the default) also has an ECR repository, `-video`, holding the video function's image. `hyperframes lambda destroy` deletes it after the stack. -This gives each FFmpeg process ten minutes. Plan, RenderChunk, and Assemble each have to finish within the function's configured timeout, at most 15 minutes, including downloads, processing, and uploads, and fit its `/tmp` storage. Measure the failing phase's time and storage use in CloudWatch. Smaller chunks or a higher `maxParallelChunks` can reduce each chunk's work; they do not divide video extraction that still happens during Plan. +### `STACK_TOO_OLD` -If planning cannot fit, split the composition into shorter, frame-aligned projects, render each as a separate job, and combine the results. Choose part lengths from measured planning times. There is no fixed maximum video length: resolution, frame rate, codec, and Lambda resources affect extraction time. +The stack was deployed by an earlier version that cannot read this version's sites. Redeploy it (`hyperframes lambda deploy`), then render again. See [Upgrading](#upgrading-from-a-version-before-shared-content-storage). -### Tearing down doesn't reclaim S3 storage +### `SITE_FILES_MISSING` or `SITE_NOT_FOUND` -The render bucket is created with CloudFormation `Retain` on delete — `hyperframes lambda destroy` (or `sam delete`) tears the function + state machine down but the bucket survives. This is intentional: it protects final-rendered MP4s from being lost when you re-deploy. To fully reclaim storage, empty + delete the bucket via the AWS console / `aws s3 rb`. +`SITE_FILES_MISSING`: some of the site's files expired from `cas/sha256/` after 30 days unused. Render from the project directory the site was created from, or run `sites create` again. `SITE_NOT_FOUND`: the bucket has no site under that id in either format; run `sites create` again and use the id it prints. ## Current limits @@ -239,17 +254,15 @@ The render bucket is created with CloudFormation `Retain` on delete — `hyperfr - Each `--region` is an independent stack. Cross-region failover is not built in. - The distributed AWS path is SDR-only. -- Plan v2 leaves eligible constant-frame-rate SDR video extraction to each - render chunk, which extracts only the frames it shows. On a 10,240 MB - function, a 26-minute 576×1024 24 fps H.264 clip plans in about 20 seconds. Each chunk downloads - the whole source file of every video it shows. Variable-frame-rate sources - and required HDR color conversions still need extraction during Plan. - Constant-frame-rate extraction uses two-minute segments; variable-frame-rate - extraction remains unsegmented. See [long-video planning timeouts](#long-video-planning-times-out) - for timeout configuration and splitting jobs that cannot fit. -- Plan, each RenderChunk, and Assemble must finish within their own configured - Lambda timeout, at most 15 minutes, and fit the function's `/tmp` storage. - For long renders, raise `--max-parallel-chunks` (`maxParallelChunks` in the SDK) so each chunk covers +- Each render chunk extracts only the video frames it shows, so the plan step + no longer grows with video length. On a 10,240 MB function, a 26-minute + 576×1024 24 fps H.264 clip plans in about 20 seconds. Each chunk reads + only the byte ranges of each video it shows, straight from the bucket. Variable-frame-rate and HDR + sources are the exception: the plan step still extracts every frame of those, + inside Lambda's 15-minute limit, in 2-minute segments, so + `FFMPEG_PROCESS_TIMEOUT_MS` applies to each segment, not to the whole clip. +- A chunk must also finish inside 15 minutes. For long renders, raise + `--max-parallel-chunks` (`maxParallelChunks` in the SDK) so each chunk covers fewer frames. ## Related topics From 16b6238a63bd7fd15c60cd943093b9554e040406 Mon Sep 17 00:00:00 2001 From: "Abdullah M. Nazif" Date: Sat, 24 Oct 2026 14:13:09 +0800 Subject: [PATCH 5/6] docs(aws-lambda): align long-video planning guidance with current extraction limits --- docs/deploy/aws-lambda.mdx | 38 +++++++++++++++++++++++++++--------- docs/packages/aws-lambda.mdx | 4 ++-- 2 files changed, 31 insertions(+), 11 deletions(-) diff --git a/docs/deploy/aws-lambda.mdx b/docs/deploy/aws-lambda.mdx index d85dfa153b3..3267e6177d6 100644 --- a/docs/deploy/aws-lambda.mdx +++ b/docs/deploy/aws-lambda.mdx @@ -231,6 +231,22 @@ Most often a Lambda cold-start chain on a many-chunk render. The Map state's res If progress doesn't advance for >10 minutes, check the Step Functions execution in the AWS console — failed Lambda invocations include the typed error name (`FONT_FETCH_FAILED`, `FFMPEG_VERSION_MISMATCH`, etc.) which short-circuits the state machine. +### Long-video planning times out + +Check the failed invocation's CloudWatch logs to distinguish Plan from RenderChunk. In current versions, `VIDEO_EXTRACTION_FAILED` with `ffmpeg_timeout` means extraction stopped producing new frames for the configured interval, which defaults to 300,000 ms. Frame progress resets that timer, so an extraction can run for longer than five minutes while it keeps producing frames. Normal frame extraction splits long ranges into segments of up to two minutes, including variable-frame-rate sources. + +Plan v2 leaves eligible constant-frame-rate SDR extraction to chunk workers after a short timing probe. Variable-frame-rate sources and required HDR color conversions still extract during Plan; the deprecated v1 path also extracts videos during Plan. Smaller chunks or a higher `maxParallelChunks` can reduce each chunk's work, but do not divide extraction that still happens during Plan. + +If the logs show that a slow extraction needs a longer gap between frames, and the work fits within the Lambda invocation budget, increase `FFMPEG_PROCESS_TIMEOUT_MS` on the deployed function that runs the failing phase. The default arm64 deployment uses the video function for Plan and RenderChunk. Setting the variable only in the terminal running `lambda render` does not configure the remote function. CDK users can configure the construct's render function before deploying: + +```ts +render.renderFunction.addEnvironment("FFMPEG_PROCESS_TIMEOUT_MS", "600000"); +``` + +This allows extraction to wait ten minutes without a new frame. Each Plan, RenderChunk, and Assemble invocation must still finish within its own configured Lambda timeout, at most 15 minutes, including downloads, processing, and uploads, and fit its `/tmp` storage. Measure the failing phase's elapsed time and storage use in CloudWatch. + +If planning cannot fit, split the composition into shorter, frame-aligned projects, render each as a separate job, and combine the results. Choose part lengths from measured planning times. There is no fixed maximum video length: resolution, frame rate, codec, and Lambda resources affect extraction time. + ### Tearing down doesn't reclaim S3 storage The render bucket is created with CloudFormation `Retain` on delete — `hyperframes lambda destroy` (or `sam delete`) tears the function + state machine down but the bucket survives. This is intentional: it protects final-rendered MP4s from being lost when you re-deploy. To fully reclaim storage, empty + delete the bucket via the AWS console / `aws s3 rb`. @@ -254,15 +270,19 @@ The stack was deployed by an earlier version that cannot read this version's sit - Each `--region` is an independent stack. Cross-region failover is not built in. - The distributed AWS path is SDR-only. -- Each render chunk extracts only the video frames it shows, so the plan step - no longer grows with video length. On a 10,240 MB function, a 26-minute - 576×1024 24 fps H.264 clip plans in about 20 seconds. Each chunk reads - only the byte ranges of each video it shows, straight from the bucket. Variable-frame-rate and HDR - sources are the exception: the plan step still extracts every frame of those, - inside Lambda's 15-minute limit, in 2-minute segments, so - `FFMPEG_PROCESS_TIMEOUT_MS` applies to each segment, not to the whole clip. -- A chunk must also finish inside 15 minutes. For long renders, raise - `--max-parallel-chunks` (`maxParallelChunks` in the SDK) so each chunk covers +- Plan v2 leaves eligible constant-frame-rate SDR video extraction to each + render chunk after a short timing probe. Each chunk extracts only the frames + it shows. On a 10,240 MB function, a 26-minute 576×1024 24 fps H.264 clip + plans in about 20 seconds. Each chunk reads only the byte ranges of each + video it shows, straight from the bucket. Variable-frame-rate sources and + required HDR color conversions still need extraction during Plan. Normal + frame extraction uses segments of up to two minutes, including variable-frame-rate + sources; `FFMPEG_PROCESS_TIMEOUT_MS` limits how long extraction can go without + producing a new frame. See [long-video planning timeouts](#long-video-planning-times-out) + for timeout configuration and splitting jobs that cannot fit. +- Plan, each RenderChunk, and Assemble must finish within their own configured + Lambda timeout, at most 15 minutes, and fit the function's `/tmp` storage. + For long renders, raise `--max-parallel-chunks` (`maxParallelChunks` in the SDK) so each chunk covers fewer frames. ## Related topics diff --git a/docs/packages/aws-lambda.mdx b/docs/packages/aws-lambda.mdx index 220d11bb598..90f14adb7e5 100644 --- a/docs/packages/aws-lambda.mdx +++ b/docs/packages/aws-lambda.mdx @@ -44,7 +44,7 @@ The Lambda adapter wraps the distributed producer primitives behind one dispatch - Downloads the project archive from S3, probes the composition, prepares audio, and publishes the plan artifacts. Plan v2 defers eligible constant-frame-rate SDR video extraction to the chunk workers; variable-frame-rate sources and required HDR color conversions still run during planning. + Fetches the project inputs from S3, probes the composition, prepares audio, and publishes the plan artifacts. Plan v2 defers eligible constant-frame-rate SDR video extraction to the chunk workers; variable-frame-rate sources and required HDR color conversions still run during planning. Step Functions fans out chunk jobs. Each Lambda invocation downloads the assets it needs, extracts its deferred video frames, renders one chunk, and uploads the result. @@ -54,7 +54,7 @@ The Lambda adapter wraps the distributed producer primitives behind one dispatch -All three phases must fit the configured per-invocation Lambda timeout, at most 15 minutes, and the function's temporary storage. Smaller chunks can reduce each worker's extraction and rendering work, but do not divide video extraction that still happens in Plan. See [long-video planning limits](/deploy/aws-lambda#long-video-planning-times-out) for per-segment and variable-frame-rate FFmpeg timeouts and how to split jobs that cannot fit. +All three phases must fit the configured per-invocation Lambda timeout, at most 15 minutes, and the function's temporary storage. Smaller chunks can reduce each worker's extraction and rendering work, but do not divide video extraction that still happens in Plan. See [long-video planning limits](/deploy/aws-lambda#long-video-planning-times-out) for FFmpeg frame-stall timeouts and how to split jobs that cannot fit. The handler uses `@sparticuz/chromium` by default, with a build-time fallback for bundling `chrome-headless-shell` directly when needed. From 24a9ba2d9339ac8ea9cf6e5a29e439dfac6fd75e Mon Sep 17 00:00:00 2001 From: "Abdullah M. Nazif" Date: Sun, 25 Oct 2026 08:08:46 +0800 Subject: [PATCH 6/6] docs(aws-lambda): identify the timeout example construct --- docs/deploy/aws-lambda.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/deploy/aws-lambda.mdx b/docs/deploy/aws-lambda.mdx index 3267e6177d6..d2fb37b894f 100644 --- a/docs/deploy/aws-lambda.mdx +++ b/docs/deploy/aws-lambda.mdx @@ -237,7 +237,7 @@ Check the failed invocation's CloudWatch logs to distinguish Plan from RenderChu Plan v2 leaves eligible constant-frame-rate SDR extraction to chunk workers after a short timing probe. Variable-frame-rate sources and required HDR color conversions still extract during Plan; the deprecated v1 path also extracts videos during Plan. Smaller chunks or a higher `maxParallelChunks` can reduce each chunk's work, but do not divide extraction that still happens during Plan. -If the logs show that a slow extraction needs a longer gap between frames, and the work fits within the Lambda invocation budget, increase `FFMPEG_PROCESS_TIMEOUT_MS` on the deployed function that runs the failing phase. The default arm64 deployment uses the video function for Plan and RenderChunk. Setting the variable only in the terminal running `lambda render` does not configure the remote function. CDK users can configure the construct's render function before deploying: +If the logs show that a slow extraction needs a longer gap between frames, and the work fits within the Lambda invocation budget, increase `FFMPEG_PROCESS_TIMEOUT_MS` on the deployed function that runs the failing phase. The default arm64 deployment uses the video function for Plan and RenderChunk. Setting the variable only in the terminal running `lambda render` does not configure the remote function. CDK users can configure the construct's render function before deploying. Here, `render` is the `HyperframesRenderStack` instance created in the CDK example above: ```ts render.renderFunction.addEnvironment("FFMPEG_PROCESS_TIMEOUT_MS", "600000");