{"record":{"id":"63174cda8e80f5ca","repo":"heygen-com/hyperframes","slug":"s3transport-missing-key-in-s3-uri-json-string","errorCode":null,"errorMessage":"[s3Transport] missing key in s3 URI: ${JSON.stringify(uri)}","messagePattern":"\\[s3Transport\\] missing key in s3 URI: (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/aws-lambda/src/s3Transport.ts","lineNumber":53,"sourceCode":"  type S3Client,\n} from \"@aws-sdk/client-s3\";\nimport * as tar from \"tar\";\n\n/** Parsed `s3://bucket/key` URI. */\nexport 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,","sourceCodeStart":35,"sourceCodeEnd":71,"githubUrl":"https://github.com/heygen-com/hyperframes/blob/c2996c8626135db5253519359d8a063d3bafad8d/packages/aws-lambda/src/s3Transport.ts#L35-L71","documentation":"Thrown by `parseS3Uri` when the URI starts with `s3://` but contains no slash after the bucket — i.e. it specifies a bucket with no key. S3 objects require both a bucket and a non-empty key; a bare `s3://bucket` cannot address an object.","triggerScenarios":"Calling `parseS3Uri(\"s3://my-bucket\")` — the portion after `s3://` has no `/`, so there is no key.","commonSituations":"Configured only a bucket name where a full object path was expected; trailing path stripped by a bad string split; env var set to the bucket root rather than a specific object key.","solutions":["Append the object key to the URI: `s3://bucket/path/to/object`.","If you meant the bucket as a location, build a URI with at least one key segment.","Validate config at load time with `parseS3Uri` in a try/catch to surface bad values early."],"exampleFix":"// before\nparseS3Uri(process.env.HF_OUTPUT_BUCKET); // \"s3://my-bucket\"\n// after\nparseS3Uri(`${process.env.HF_OUTPUT_BUCKET}/renders/${renderId}/out.mp4`);","handlingStrategy":"validation","validationCode":"function assertHasKey(uri: string): void {\n  const rest = uri.slice(\"s3://\".length);\n  if (!rest.includes(\"/\")) {\n    throw new Error(`s3 URI is missing a key: ${uri}`);\n  }\n}","typeGuard":"function isS3UriWithKey(value: unknown): value is string {\n  return typeof value === \"string\" && value.startsWith(\"s3://\") && value.slice(5).includes(\"/\");\n}","tryCatchPattern":null,"preventionTips":["Build URIs from `{bucket, key}` via formatS3Uri so a missing key surfaces as undefined input.","Always pair a bucket config with an explicit key template.","Unit-test config values with the same parser the transport uses."],"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"}