heygen-com/hyperframes · error · Error
[s3Transport] s3 GetObject returned empty body for ${uri}
Error message
[s3Transport] s3 GetObject returned empty body for ${uri} What it means
Thrown by `downloadS3ObjectToFile` when a `GetObjectCommand` succeeds (HTTP 200) but `response.Body` is undefined/null. S3 normally returns a stream; an absent body indicates a zero-byte object, an SDK deserialization quirk, or an edge condition, and the function refuses to write an empty file silently.
Source
Thrown at packages/aws-lambda/src/s3Transport.ts:78
return { bucket, key };
}
/** Build `s3://bucket/key` from a location. */
export function formatS3Uri(loc: S3Location): string {
return `s3://${loc.bucket}/${loc.key}`;
}
/** Stream an S3 object to a local file path. Throws if the body is missing. */
export async function downloadS3ObjectToFile(
client: S3Client,
uri: string,
destPath: string,
): Promise<void> {
const { bucket, key } = parseS3Uri(uri);
const response = await client.send(new GetObjectCommand({ Bucket: bucket, Key: key }));
const body = response.Body as NodeJS.ReadableStream | undefined;
if (!body) {
throw new Error(`[s3Transport] s3 GetObject returned empty body for ${uri}`);
}
mkdirSync(dirname(destPath), { recursive: true });
await pipeline(body, createWriteStream(destPath));
}
/** Download and verify an immutable plan-v2 artifact before materialization. */
export async function downloadS3ObjectToFileVerified(
client: S3Client,
uri: string,
destPath: string,
expectedSha256: string,
): Promise<void> {
assertSha256(expectedSha256);
await downloadS3ObjectToFile(client, uri, destPath);
const actual = await sha256File(destPath);
if (actual !== expectedSha256) {
rmSync(destPath, { force: true });
const error = new Error(View on GitHub (pinned to c2996c8626)
Solutions
- Inspect the object in S3 — `aws s3 ls s3://bucket/key` — to confirm it has content; re-upload if it's a 0-byte placeholder.
- Verify the key is correct and the object exists in the expected region/bucket.
- If a marker object is legitimate, special-case it at the caller rather than calling downloadS3ObjectToFile.
- Align @aws-sdk/client-s3 versions between producer and Lambda.
Example fix
// before: caller assumes non-empty body
await downloadS3ObjectToFile(s3, uri, dest);
// after: guard marker objects
const head = await s3.send(new HeadObjectCommand({ Bucket, Key }));
if ((head.ContentLength ?? 0) === 0) continue;
await downloadS3ObjectToFile(s3, uri, dest); Defensive patterns
Strategy: validation
Validate before calling
import { HeadObjectCommand } from "@aws-sdk/client-s3";
async function assertObjectHasBody(client: S3Client, bucket: string, key: string): Promise<void> {
const head = await client.send(new HeadObjectCommand({ Bucket: bucket, Key: key }));
if ((head.ContentLength ?? 0) === 0) {
throw new Error(`s3 object is empty: s3://${bucket}/${key}`);
}
} Try / catch
try {
await downloadS3ObjectToFile(client, uri, dest);
} catch (err) {
if (err instanceof Error && /returned empty body/.test(err.message)) {
// treat as a missing/stale object; re-upload or skip if it's a marker
}
throw err;
} Prevention
- Head the object before downloading if a 0-byte marker is a possibility.
- Ensure producers always write non-empty bodies (refuse to upload 0-byte artifacts).
- Align @aws-sdk/client-s3 versions across producer and Lambda.
When it happens
Trigger: `downloadS3ObjectToFile` where `client.send(new GetObjectCommand({...}))` returns a response with no `Body` — e.g. the object is a 0-byte marker, or the SDK version/model returned an unexpected shape.
Common situations: Object was created as a 0-byte marker (directory placeholder); an interrupted multipart upload left a partial object; SDK version mismatch where `Body` is wrapped differently; S3 EventBridge notification referencing a not-yet-consistent object.
Related errors
- [s3Transport] expected s3:// URI, got: ${JSON.stringify(uri)
- [s3Transport] missing key in s3 URI: ${JSON.stringify(uri)}
- [s3Transport] empty bucket or key in s3 URI: ${JSON.stringif
- [s3Transport] upload source missing: ${localPath}
- [deploySite] projectDir is not a directory: ${opts.projectDi
AI-assisted analysis of heygen-com/hyperframes@c2996c8626 (2026-08-12).
Data as JSON: /api/errors/77d1abb0b70af04c.
Report an issue: GitHub.