{"record":{"id":"77d1abb0b70af04c","repo":"heygen-com/hyperframes","slug":"s3transport-s3-getobject-returned-empty-body-for","errorCode":null,"errorMessage":"[s3Transport] s3 GetObject returned empty body for ${uri}","messagePattern":"\\[s3Transport\\] s3 GetObject returned empty body for (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/aws-lambda/src/s3Transport.ts","lineNumber":78,"sourceCode":"  return { bucket, key };\n}\n\n/** Build `s3://bucket/key` from a location. */\nexport function formatS3Uri(loc: S3Location): string {\n  return `s3://${loc.bucket}/${loc.key}`;\n}\n\n/** Stream an S3 object to a local file path. Throws if the body is missing. */\nexport async function downloadS3ObjectToFile(\n  client: S3Client,\n  uri: string,\n  destPath: string,\n): Promise<void> {\n  const { bucket, key } = parseS3Uri(uri);\n  const response = await client.send(new GetObjectCommand({ Bucket: bucket, Key: key }));\n  const body = response.Body as NodeJS.ReadableStream | undefined;\n  if (!body) {\n    throw new Error(`[s3Transport] s3 GetObject returned empty body for ${uri}`);\n  }\n  mkdirSync(dirname(destPath), { recursive: true });\n  await pipeline(body, createWriteStream(destPath));\n}\n\n/** Download and verify an immutable plan-v2 artifact before materialization. */\nexport async function downloadS3ObjectToFileVerified(\n  client: S3Client,\n  uri: string,\n  destPath: string,\n  expectedSha256: string,\n): Promise<void> {\n  assertSha256(expectedSha256);\n  await downloadS3ObjectToFile(client, uri, destPath);\n  const actual = await sha256File(destPath);\n  if (actual !== expectedSha256) {\n    rmSync(destPath, { force: true });\n    const error = new Error(","sourceCodeStart":60,"sourceCodeEnd":96,"githubUrl":"https://github.com/heygen-com/hyperframes/blob/c2996c8626135db5253519359d8a063d3bafad8d/packages/aws-lambda/src/s3Transport.ts#L60-L96","documentation":"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.","triggerScenarios":"`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.","commonSituations":"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.","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."],"exampleFix":"// before: caller assumes non-empty body\nawait downloadS3ObjectToFile(s3, uri, dest);\n// after: guard marker objects\nconst head = await s3.send(new HeadObjectCommand({ Bucket, Key }));\nif ((head.ContentLength ?? 0) === 0) continue;\nawait downloadS3ObjectToFile(s3, uri, dest);","handlingStrategy":"validation","validationCode":"import { HeadObjectCommand } from \"@aws-sdk/client-s3\";\nasync function assertObjectHasBody(client: S3Client, bucket: string, key: string): Promise<void> {\n  const head = await client.send(new HeadObjectCommand({ Bucket: bucket, Key: key }));\n  if ((head.ContentLength ?? 0) === 0) {\n    throw new Error(`s3 object is empty: s3://${bucket}/${key}`);\n  }\n}","typeGuard":null,"tryCatchPattern":"try {\n  await downloadS3ObjectToFile(client, uri, dest);\n} catch (err) {\n  if (err instanceof Error && /returned empty body/.test(err.message)) {\n    // treat as a missing/stale object; re-upload or skip if it's a marker\n  }\n  throw err;\n}","preventionTips":["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."],"tags":["s3","transport","download","validation","aws-sdk"],"backgroundTag":null,"analyzedSha":"c2996c8626135db5253519359d8a063d3bafad8d","analyzedAt":"2026-08-12T22:18:56.877Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}