heygen-com/hyperframes · error
[renderToLambda] either siteHandle or projectDir must be sup
Error message
[renderToLambda] either siteHandle or projectDir must be supplied
What it means
Thrown by renderToLambda when BOTH opts.siteHandle and opts.projectDir are absent. The function needs a project to render: either a previously-deployed SiteHandle (skips the tar+PUT) or a local projectDir (deploySite runs inline). Supplying neither means there is nothing to render, and the guard prevents a downstream undefined dereference in deploySite. Passing one of the two resolves it.
Source
Thrown at packages/aws-lambda/src/sdk/renderToLambda.ts:92
bucketName: string;
stateMachineArn: string;
outputS3Uri: string;
projectS3Uri: string;
startedAt: string;
}
// fallow-ignore-next-line complexity
export async function renderToLambda(opts: RenderToLambdaOptions): Promise<RenderHandle> {
validateDistributedRenderConfig(opts.config);
if (!opts.bucketName) {
throw new Error("[renderToLambda] bucketName is required");
}
if (!opts.stateMachineArn) {
throw new Error("[renderToLambda] stateMachineArn is required");
}
if (!opts.siteHandle && !opts.projectDir) {
throw new Error("[renderToLambda] either siteHandle or projectDir must be supplied");
}
const executionName = opts.executionName ?? `hf-render-${randomUUID()}`;
const ext = formatExtension(opts.config.format);
const outputKey = opts.outputKey ?? `renders/${executionName}/output${ext}`;
const planOutputS3Prefix = formatS3Uri({
bucket: opts.bucketName,
key: `renders/${executionName}/`,
});
const outputS3Uri = formatS3Uri({ bucket: opts.bucketName, key: outputKey });
const site =
opts.siteHandle ??
(await deploySite({
projectDir: opts.projectDir as string,
bucketName: opts.bucketName,
region: opts.region,
s3: opts.s3,View on GitHub (pinned to c2996c8626)
Solutions
- Always provide projectDir as a baseline; layer siteHandle on top when you have a cached handle.
- Restructure the caller so exactly one branch runs: if (siteHandle) use it, else use projectDir.
- Add a TS-level assertion or branded type so the 'both undefined' state is unrepresentable.
- Default projectDir to a known directory when no siteHandle exists.
Example fix
// before
const handle = cache.get(id); // undefined on miss
await renderToLambda({ config, bucketName, stateMachineArn, siteHandle: handle });
// after
const opts = handle
? { siteHandle: handle }
: { projectDir: './dist' };
await renderToLambda({ config, bucketName, stateMachineArn, ...opts }); Defensive patterns
Strategy: validation
Validate before calling
function assertRenderSource(opts: {
siteHandle?: unknown; projectDir?: string;
}): void {
if (!opts.siteHandle && !opts.projectDir) {
throw new Error('either siteHandle or projectDir must be supplied');
}
} Type guard
const hasRenderSource = (o: { siteHandle?: unknown; projectDir?: string }): boolean =>
Boolean(o.siteHandle) || Boolean(o.projectDir); Prevention
- Structure the caller so exactly one of siteHandle/projectDir is set on every branch.
- Default to projectDir when no cached handle exists.
- Make the 'both undefined' state unrepresentable with a discriminated union.
When it happens
Trigger: Caller assumes projectDir is required and siteHandle optional but passes neither; siteHandle is conditionally set and the condition evaluated false; an options builder that omits both when a feature flag is off.
Common situations: Branching logic that sets siteHandle only on cache-hit and projectDir only on cache-miss but leaves a path where neither is set; spreading a partial config object; TS optional fields left undefined at runtime despite the type.
Related errors
- [renderToLambda] bucketName is required
- [renderToLambda] stateMachineArn is required
- [getRenderProgress] executionArn is required
- [validateConfig] config: Step Functions execution input is $
- [s3Transport] tar source must be an existing directory: ${so
AI-assisted analysis of heygen-com/hyperframes@c2996c8626 (2026-08-12).
Data as JSON: /api/errors/4a1a4d4cd8ddabf4.
Report an issue: GitHub.