{"record":{"id":"ca6d4156aefbc4d0","repo":"heygen-com/hyperframes","slug":"s3transport-empty-bucket-or-key-in-s3-uri-jso","errorCode":null,"errorMessage":"[s3Transport] empty bucket or key in s3 URI: ${JSON.stringify(uri)}","messagePattern":"\\[s3Transport\\] empty bucket or key in s3 URI: (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/aws-lambda/src/s3Transport.ts","lineNumber":58,"sourceCode":"export interface S3Location {\n  bucket: string;\n  key: string;\n}\n\n/** Parse `s3://bucket/key/path` → `{ bucket, key }`. Throws on malformed input. */\nexport function parseS3Uri(uri: string): S3Location {\n  if (!uri.startsWith(\"s3://\")) {\n    throw new Error(`[s3Transport] expected s3:// URI, got: ${JSON.stringify(uri)}`);\n  }\n  const rest = uri.slice(\"s3://\".length);\n  const slash = rest.indexOf(\"/\");\n  if (slash === -1) {\n    throw new Error(`[s3Transport] missing key in s3 URI: ${JSON.stringify(uri)}`);\n  }\n  const bucket = rest.slice(0, slash);\n  const key = rest.slice(slash + 1);\n  if (!bucket || !key) {\n    throw new Error(`[s3Transport] empty bucket or key in s3 URI: ${JSON.stringify(uri)}`);\n  }\n  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;","sourceCodeStart":40,"sourceCodeEnd":76,"githubUrl":"https://github.com/heygen-com/hyperframes/blob/c2996c8626135db5253519359d8a063d3bafad8d/packages/aws-lambda/src/s3Transport.ts#L40-L76","documentation":"Thrown by `parseS3Uri` when the URI parses to an empty bucket or empty key — i.e. a slash exists but one side is blank, such as `s3:///key` or `s3://bucket/`. Both bucket and key must be non-empty to address an object.","triggerScenarios":"`parseS3Uri(\"s3:///key\")` (empty bucket), `parseS3Uri(\"s3://bucket/\")` (empty key), or `s3:///` (both empty).","commonSituations":"String concatenation that produced an empty bucket or key segment; an env var with a trailing slash treated as a complete URI; a path join that dropped the key; misconfigured prefix with no object path.","solutions":["Construct URIs with `formatS3Uri({bucket, key})` so empty parts surface as undefined inputs rather than malformed URIs.","Validate that `bucket` and `key` are non-empty strings before formatting.","Strip trailing slashes from config-supplied prefixes and re-append the key explicitly."],"exampleFix":"// before\nparseS3Uri(`s3://${bucket}/`);\n// after\nimport { formatS3Uri } from \"./s3Transport\";\nformatS3Uri({ bucket, key: `renders/${renderId}/out.mp4` });","handlingStrategy":"validation","validationCode":"function parseS3UriSafe(uri: string): { bucket: string; key: string } | null {\n  if (!uri.startsWith(\"s3://\")) return null;\n  const rest = uri.slice(5);\n  const slash = rest.indexOf(\"/\");\n  if (slash === -1) return null;\n  const bucket = rest.slice(0, slash);\n  const key = rest.slice(slash + 1);\n  if (!bucket || !key) return null;\n  return { bucket, key };\n}","typeGuard":"function isS3Location(value: unknown): value is { bucket: string; key: string } {\n  return (\n    value !== null &&\n    typeof value === \"object\" &&\n    typeof (value as any).bucket === \"string\" && (value as any).bucket.length > 0 &&\n    typeof (value as any).key === \"string\" && (value as any).key.length > 0\n  );\n}","tryCatchPattern":null,"preventionTips":["Prefer formatS3Uri over hand-built strings to avoid empty segments.","Strip trailing slashes from prefixes before appending keys.","Validate non-empty bucket and key at the config boundary."],"tags":["s3","transport","uri","validation"],"backgroundTag":null,"analyzedSha":"c2996c8626135db5253519359d8a063d3bafad8d","analyzedAt":"2026-08-12T22:18:56.877Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}