heygen-com/hyperframes · error · Error
freeze failed: HTTP ${res.status}
Error message
freeze failed: HTTP ${res.status} What it means
Thrown by freezeUrl after the fetch when res.ok is false. The URL passed the host allowlist and the request was issued, but the response status was not 2xx — most often because the figma CDN URL is short-lived and expired, or because figma's S3 returned a 403/404 for a stale signed URL. freezeUrl surfaces the raw status so the caller can decide whether to re-render (get a fresh URL) or give up.
Source
Thrown at packages/core/src/figma/freeze.ts:54
* crafted manifest/config URL (metadata endpoints, internal services).
*/
export function isAllowedFreezeUrl(url: string): boolean {
let parsed: URL;
try {
parsed = new URL(url);
} catch {
return false;
}
if (parsed.protocol !== "https:") return false;
const host = parsed.hostname;
return host === "figma.com" || host.endsWith(".figma.com") || host.endsWith(".amazonaws.com");
}
export async function freezeUrl(url: string, destPath: string): Promise<number> {
if (!isAllowedFreezeUrl(url))
throw new Error(`freeze failed: refusing non-figma url ${url} (https + figma hosts only)`);
const res = await fetch(url);
if (!res.ok) throw new Error(`freeze failed: HTTP ${res.status}`);
const declared = Number(res.headers.get("content-length") ?? 0);
if (exceedsFreezeCap(declared))
throw new Error(`freeze failed: content-length ${declared} exceeds ${MAX_FREEZE_BYTES} cap`);
return freezeBytes(new Uint8Array(await res.arrayBuffer()), destPath);
}
export function freezeLocalFile(srcPath: string, destPath: string): void {
const size = statSync(srcPath).size;
if (exceedsFreezeCap(size))
throw new Error(`freeze failed: ${size} bytes exceeds ${MAX_FREEZE_BYTES} cap`);
mkdirSync(dirname(destPath), { recursive: true });
copyFileSync(srcPath, destPath);
}
View on GitHub (pinned to c2996c8626)
Solutions
- Re-call renderNodes/renderNode to mint a fresh short-lived URL, then freeze it immediately.
- Never persist render URLs across sessions — always re-render and freeze in the same run.
- If the status is 5xx, retry after a short wait (CDN transient).
- Confirm no proxy is stripping the URL's signed query parameters.
Example fix
// before — render URL stored and used later, TTL expired
const { url } = await client.renderNode(ref, { format: 'png' });
// ...time passes...
await freezeUrl(url, dest); // HTTP 403
// after — render and freeze in the same tick
const { url } = await client.renderNode(ref, { format: 'png' });
await freezeUrl(url, dest); Defensive patterns
Strategy: retry
Try / catch
async function freezeWithRefresh(url: string, dest: string, refresh: () => Promise<string>): Promise<number> {
try {
return await freezeUrl(url, dest);
} catch (err) {
if (err instanceof Error && /freeze failed: HTTP/.test(err.message)) {
const fresh = await refresh(); // re-call renderNodes for a new signed URL
return await freezeUrl(fresh, dest);
}
throw err;
}
} Prevention
- Render and freeze in the same tick — figma CDN URLs are short-lived.
- Never persist render URLs across sessions; always re-render.
- Confirm no proxy strips the signed query string from the URL.
When it happens
Trigger: A figma render URL used after its short TTL expired (figma CDN URLs are signed and time-limited); a 403 from S3 for a revoked/aged signed URL; a 404 because the asset was garbage-collected; a network proxy returning a 502 for the figma CDN.
Common situations: Calling renderNodes, holding the returned URL for minutes/hours, then calling freezeUrl on it; saving a render URL to a manifest and reusing it in a later session; a flaky corporate proxy between the CLI and figma's CDN.
Related errors
- figma render download failed: HTTP ${res.status}
- freeze failed: empty bytes
- freeze failed: ${bytes.length} bytes exceeds ${MAX_FREEZE_BY
- freeze failed: refusing non-figma url ${url} (https + figma
- freeze failed: content-length ${declared} exceeds ${MAX_FREE
AI-assisted analysis of heygen-com/hyperframes@c2996c8626 (2026-08-12).
Data as JSON: /api/errors/dfd2a1fa48e8a525.
Report an issue: GitHub.