heygen-com/hyperframes · error · Error

[handler] chunk URI at index ${i} is empty

Error message

[handler] chunk URI at index ${i} is empty

What it means

Thrown by `downloadChunks` during the `assemble` action when a chunk URI in the input array is empty (falsy). Each chunk URI must resolve to an S3 object; an empty entry usually means the planner emitted a sparse array or a chunk index was skipped.

Source

Thrown at packages/aws-lambda/src/handler.ts:752

async function downloadChunkObjects(
  s3: S3Client,
  uris: string[],
  workDir: string,
  format: DistributedFormat,
): Promise<string[]> {
  const chunksDir = join(workDir, "chunks");
  mkdirSync(chunksDir, { recursive: true });
  // Each chunk is an independent S3 GET (+ untar for png-sequence). Run
  // them in parallel — assemble's wall-clock is otherwise dominated by
  // `Σ chunk-download-ms` instead of `max(chunk-download-ms)`. Preserve
  // the input order by writing into a pre-sized array rather than
  // pushing as each task settles.
  const local: string[] = new Array<string>(uris.length);
  await Promise.all(
    uris.map(async (uri, i) => {
      if (!uri) {
        throw new Error(`[handler] chunk URI at index ${i} is empty`);
      }
      const { key } = parseS3Uri(uri);
      const localPath = join(chunksDir, basename(key));
      await downloadS3ObjectToFile(s3, uri, localPath);
      if (format === "png-sequence") {
        const dirPath = join(chunksDir, `frames-${pad(i)}`);
        await untarDirectory(localPath, dirPath);
        local[i] = dirPath;
      } else {
        local[i] = localPath;
      }
    }),
  );
  return local;
}

// ── Helpers ─────────────────────────────────────────────────────────────────

View on GitHub (pinned to c2996c8626)

Solutions

  1. Inspect the assemble event's chunk URI array in CloudWatch to locate the gap.
  2. Ensure the planner emits a contiguous, fully-populated URI array (no skipped indices).
  3. Re-run `plan` to regenerate a complete chunk list before `assemble`.
  4. If a chunk legitimately has no output, define the protocol for that slot rather than leaving it empty.

Example fix

// before: sparse chunk list
const uris = ["s3://b/chunk-0", "", "s3://b/chunk-2"];
// after: contiguous, fully populated
const uris = ["s3://b/chunk-0", "s3://b/chunk-1", "s3://b/chunk-2"];
Defensive patterns

Strategy: validation

Validate before calling

function assertChunkUris(uris: unknown[]): void {
  uris.forEach((uri, i) => {
    if (typeof uri !== "string" || uri.length === 0) {
      throw new Error(`chunk URI at index ${i} is empty`);
    }
  });
}
// call before downloadChunks

Type guard

function isNonEmptyStringArray(value: unknown): value is string[] {
  return Array.isArray(value) && value.every((v) => typeof v === "string" && v.length > 0);
}

Prevention

When it happens

Trigger: `downloadChunks(uris, ...)` with an element that is `""`, `null`, or `undefined` at index `i` — i.e. the assemble event's chunk-URI list is sparse or has a gap.

Common situations: Planner bug that built a sparse chunk list when a chunk produced no output; renderChunk result aggregation that left a hole at a failed/empty chunk index; JSON round-trip that dropped an undefined element.

Related errors


AI-assisted analysis of heygen-com/hyperframes@c2996c8626 (2026-08-12). Data as JSON: /api/errors/bf3bf55545d6749b. Report an issue: GitHub.