heygen-com/hyperframes · error · Error
[s3Transport] empty bucket or key in s3 URI: ${JSON.stringif
Error message
[s3Transport] empty bucket or key in s3 URI: ${JSON.stringify(uri)} What it means
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.
Source
Thrown at packages/aws-lambda/src/s3Transport.ts:58
export interface S3Location {
bucket: string;
key: string;
}
/** Parse `s3://bucket/key/path` → `{ bucket, key }`. Throws on malformed input. */
export function parseS3Uri(uri: string): S3Location {
if (!uri.startsWith("s3://")) {
throw new Error(`[s3Transport] expected s3:// URI, got: ${JSON.stringify(uri)}`);
}
const rest = uri.slice("s3://".length);
const slash = rest.indexOf("/");
if (slash === -1) {
throw new Error(`[s3Transport] missing key in s3 URI: ${JSON.stringify(uri)}`);
}
const bucket = rest.slice(0, slash);
const key = rest.slice(slash + 1);
if (!bucket || !key) {
throw new Error(`[s3Transport] empty bucket or key in s3 URI: ${JSON.stringify(uri)}`);
}
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;View on GitHub (pinned to c2996c8626)
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.
Example fix
// before
parseS3Uri(`s3://${bucket}/`);
// after
import { formatS3Uri } from "./s3Transport";
formatS3Uri({ bucket, key: `renders/${renderId}/out.mp4` }); Defensive patterns
Strategy: validation
Validate before calling
function parseS3UriSafe(uri: string): { bucket: string; key: string } | null {
if (!uri.startsWith("s3://")) return null;
const rest = uri.slice(5);
const slash = rest.indexOf("/");
if (slash === -1) return null;
const bucket = rest.slice(0, slash);
const key = rest.slice(slash + 1);
if (!bucket || !key) return null;
return { bucket, key };
} Type guard
function isS3Location(value: unknown): value is { bucket: string; key: string } {
return (
value !== null &&
typeof value === "object" &&
typeof (value as any).bucket === "string" && (value as any).bucket.length > 0 &&
typeof (value as any).key === "string" && (value as any).key.length > 0
);
} Prevention
- 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.
When it happens
Trigger: `parseS3Uri("s3:///key")` (empty bucket), `parseS3Uri("s3://bucket/")` (empty key), or `s3:///` (both empty).
Common situations: 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.
Related errors
- [s3Transport] expected s3:// URI, got: ${JSON.stringify(uri)
- [s3Transport] missing key in s3 URI: ${JSON.stringify(uri)}
- [s3Transport] s3 GetObject returned empty body for ${uri}
- [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/ca6d4156aefbc4d0.
Report an issue: GitHub.