heygen-com/hyperframes · error · Error

freeze failed: empty bytes

Error message

freeze failed: empty bytes

What it means

Thrown by freezeBytes when bytes.length === 0. freezeBytes is the low-level writer that persists downloaded or supplied asset bytes to disk so renders never re-fetch from figma; an empty payload means there is nothing to persist and almost certainly indicates an upstream bug (an empty download, a figma CDN glitch, or a caller passing an empty Uint8Array). Rejecting it here prevents a zero-byte file from being written and later treated as a valid frozen asset.

Source

Thrown at packages/core/src/figma/freeze.ts:17

/**
 * "Freeze" = write asset bytes to local disk permanently so renders never
 * re-fetch from figma (design spec §5) — not Object.freeze.
 */

import { copyFileSync, mkdirSync, rmSync, statSync, writeFileSync } from "node:fs";
import { dirname } from "node:path";

// ponytail: bound the write so a hostile/runaway source can't fill the disk.
export const MAX_FREEZE_BYTES = 256 * 1024 * 1024;

export function exceedsFreezeCap(byteLength: number): boolean {
  return byteLength > MAX_FREEZE_BYTES;
}

export function freezeBytes(bytes: Uint8Array, destPath: string): number {
  if (bytes.length === 0) throw new Error("freeze failed: empty bytes");
  if (exceedsFreezeCap(bytes.length))
    throw new Error(`freeze failed: ${bytes.length} bytes exceeds ${MAX_FREEZE_BYTES} cap`);
  mkdirSync(dirname(destPath), { recursive: true });
  // Exclusive create; on EEXIST remove and retry — never write through an
  // existing file or planted symlink (CodeQL js/insecure-temporary-file).
  try {
    writeFileSync(destPath, bytes, { flag: "wx" });
  } catch (err) {
    if ((err as NodeJS.ErrnoException).code !== "EEXIST") throw err;
    rmSync(destPath);
    writeFileSync(destPath, bytes, { flag: "wx" });
  }
  return bytes.length;
}

/**
 * Only figma-owned hosts may be frozen from a URL — render/CDN responses
 * come from figma.com subdomains or figma's S3 buckets. Blocks SSRF via a

View on GitHub (pinned to c2996c8626)

Solutions

  1. Re-fetch the source — empty bytes from a figma CDN URL are usually transient.
  2. Check the caller: log res.headers and res.status before calling freezeBytes to confirm the download actually had a body.
  3. If you genuinely may receive empty payloads, guard before calling: if (bytes.length === 0) skip or throw a clearer upstream error.

Example fix

// before — passes whatever arrayBuffer returns, even if empty
await freezeBytes(new Uint8Array(await res.arrayBuffer()), dest);

// after — guard the empty case with a descriptive upstream error
const buf = new Uint8Array(await res.arrayBuffer());
if (buf.length === 0) throw new Error(`empty download from ${url}`);
await freezeBytes(buf, dest);
Defensive patterns

Strategy: validation

Validate before calling

export function assertNonEmpty(bytes: Uint8Array): void {
  if (bytes.length === 0) throw new Error('received empty payload — upstream download produced 0 bytes');
}
// call before freezeBytes
assertNonEmpty(buf);
await freezeBytes(buf, dest);

Try / catch

try {
  await freezeBytes(buf, dest);
} catch (err) {
  if (err instanceof Error && /empty bytes/.test(err.message)) {
    // re-fetch the source, or skip this asset
  } else throw err;
}

Prevention

When it happens

Trigger: freezeUrl downloading a 200 response with an empty body; a caller building a Uint8Array from a failed arrayBuffer() that resolved to nothing; freezeBytes called directly with new Uint8Array(0); a figma image fill whose CDN URL returns empty content.

Common situations: A transient CDN issue returning an empty 200; a code path that allocates a buffer but never fills it; an upstream fetch whose res.arrayBuffer() returned 0 bytes due to a network reset mid-stream.

Related errors


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