heygen-com/hyperframes · error
[s3Transport] tar source must be an existing directory: ${so
Error message
[s3Transport] tar source must be an existing directory: ${sourceDir} What it means
Thrown by tarDirectory when the sourceDir argument either does not exist or is not a directory (isDirectory() false). tarDirectory is the pack step used by deploySite and by the Lambda handler to gzip a planDir or project tree. Because the tar npm package is invoked with cwd: sourceDir, a non-directory path would cause a confusing ENOTDIR error inside the tar stream; this guard fails fast with the actual offending path.
Source
Thrown at packages/aws-lambda/src/s3Transport.ts:272
function isS3PreconditionFailed(error: unknown): boolean {
if (!isRecord(error)) return false;
const metadata = isRecord(error.$metadata) ? error.$metadata : undefined;
return error.name === "PreconditionFailed" || metadata?.httpStatusCode === 412;
}
function isRecord(value: unknown): value is Record<string, unknown> {
return value !== null && typeof value === "object" && !Array.isArray(value);
}
/**
* Pack a directory into a `.tar.gz` at `destTarball`. Uses the `tar` npm
* package (pure JS over `node:zlib`) rather than spawning a system tar
* binary — the AWS Lambda Node 22 base image ships a minimal set of
* userland tools and does NOT include `tar` in `/usr/bin`.
*/
export async function tarDirectory(sourceDir: string, destTarball: string): Promise<void> {
if (!existsSync(sourceDir) || !statSync(sourceDir).isDirectory()) {
throw new Error(`[s3Transport] tar source must be an existing directory: ${sourceDir}`);
}
mkdirSync(dirname(destTarball), { recursive: true });
await tar.create({ gzip: true, file: destTarball, cwd: sourceDir }, ["."]);
}
/**
* Extract a `.tar.gz` produced by {@link tarDirectory} into `destDir`.
* The directory is created (or cleared) before extraction so a retried
* invocation doesn't observe stale files from a prior run on the same
* warm Lambda container.
*/
export async function untarDirectory(tarballPath: string, destDir: string): Promise<void> {
if (!existsSync(tarballPath)) {
throw new Error(`[s3Transport] tarball missing: ${tarballPath}`);
}
// Wipe target so the warm container's prior planDir doesn't bleed into
// the new invocation. Lambda re-uses /tmp across invocations on the same
// container.View on GitHub (pinned to c2996c8626)
Solutions
- Log statSync(sourceDir) or readdirSync before the call to confirm the path resolves to a directory in the current cwd.
- Use an absolute path (e.g. path.join(os.tmpdir(), ...)) rather than a relative one, especially inside Lambda.
- Ensure the directory-creation step (mkdirSync recursive) completed before tarDirectory is invoked.
- If the path is a symlink, resolve it first with fs.realpathSync to catch broken links.
Example fix
// before
await tarDirectory(projectDir, tarball);
// after
const abs = path.resolve(projectDir);
if (!existsSync(abs) || !statSync(abs).isDirectory()) {
throw new Error(`projectDir not a directory: ${abs}`);
}
await tarDirectory(abs, tarball); Defensive patterns
Strategy: validation
Validate before calling
import { existsSync, statSync } from 'node:fs';
import { resolve } from 'node:path';
function assertTarrableDir(sourceDir: string): string {
const abs = resolve(sourceDir);
if (!existsSync(abs) || !statSync(abs).isDirectory()) {
throw new Error(`not a directory: ${abs}`);
}
return abs;
} Type guard
import { statSync } from 'node:fs';
const isDirectory = (p: string): boolean => {
try { return statSync(p).isDirectory(); } catch { return false; }
}; Prevention
- Resolve sourceDir to an absolute path before the call.
- Await the directory-creation step before tarring.
- Use realpathSync to detect broken symlinks before tar.
When it happens
Trigger: deploySite calls tarDirectory with opts.projectDir; the Lambda handler calls it with a planDir path. The error fires when the path points to a regular file, a broken symlink, or a path that was never created. Also triggered when a relative path resolves against the wrong working directory (Lambda cwd vs /tmp).
Common situations: Passing a tarball path instead of its unpacked directory; a planDir cleanup race where rmSync removed the dir between existence check and tar; relative paths on Lambda where cwd is /var/task but the artifact is under /tmp; symlink to a deleted target.
Related errors
- [s3Transport] tarball missing: ${tarballPath}
- [deploySite] projectDir is not a directory: ${opts.projectDi
- [s3Transport] upload source missing: ${localPath}
- [getRenderProgress] executionArn is required
- [renderToLambda] bucketName is required
AI-assisted analysis of heygen-com/hyperframes@c2996c8626 (2026-08-12).
Data as JSON: /api/errors/0cc5e062101e5353.
Report an issue: GitHub.