{"record":{"id":"dfd2a1fa48e8a525","repo":"heygen-com/hyperframes","slug":"freeze-failed-http-res-status","errorCode":null,"errorMessage":"freeze failed: HTTP ${res.status}","messagePattern":"freeze failed: HTTP (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/core/src/figma/freeze.ts","lineNumber":54,"sourceCode":" * crafted manifest/config URL (metadata endpoints, internal services).\n */\nexport function isAllowedFreezeUrl(url: string): boolean {\n  let parsed: URL;\n  try {\n    parsed = new URL(url);\n  } catch {\n    return false;\n  }\n  if (parsed.protocol !== \"https:\") return false;\n  const host = parsed.hostname;\n  return host === \"figma.com\" || host.endsWith(\".figma.com\") || host.endsWith(\".amazonaws.com\");\n}\n\nexport async function freezeUrl(url: string, destPath: string): Promise<number> {\n  if (!isAllowedFreezeUrl(url))\n    throw new Error(`freeze failed: refusing non-figma url ${url} (https + figma hosts only)`);\n  const res = await fetch(url);\n  if (!res.ok) throw new Error(`freeze failed: HTTP ${res.status}`);\n  const declared = Number(res.headers.get(\"content-length\") ?? 0);\n  if (exceedsFreezeCap(declared))\n    throw new Error(`freeze failed: content-length ${declared} exceeds ${MAX_FREEZE_BYTES} cap`);\n  return freezeBytes(new Uint8Array(await res.arrayBuffer()), destPath);\n}\n\nexport function freezeLocalFile(srcPath: string, destPath: string): void {\n  const size = statSync(srcPath).size;\n  if (exceedsFreezeCap(size))\n    throw new Error(`freeze failed: ${size} bytes exceeds ${MAX_FREEZE_BYTES} cap`);\n  mkdirSync(dirname(destPath), { recursive: true });\n  copyFileSync(srcPath, destPath);\n}\n","sourceCodeStart":36,"sourceCodeEnd":68,"githubUrl":"https://github.com/heygen-com/hyperframes/blob/c2996c8626135db5253519359d8a063d3bafad8d/packages/core/src/figma/freeze.ts#L36-L68","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before — render URL stored and used later, TTL expired\nconst { url } = await client.renderNode(ref, { format: 'png' });\n// ...time passes...\nawait freezeUrl(url, dest); // HTTP 403\n\n// after — render and freeze in the same tick\nconst { url } = await client.renderNode(ref, { format: 'png' });\nawait freezeUrl(url, dest);","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"async function freezeWithRefresh(url: string, dest: string, refresh: () => Promise<string>): Promise<number> {\n  try {\n    return await freezeUrl(url, dest);\n  } catch (err) {\n    if (err instanceof Error && /freeze failed: HTTP/.test(err.message)) {\n      const fresh = await refresh(); // re-call renderNodes for a new signed URL\n      return await freezeUrl(fresh, dest);\n    }\n    throw err;\n  }\n}","preventionTips":["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."],"tags":["figma","freeze","network","cdn","assets"],"backgroundTag":null,"analyzedSha":"c2996c8626135db5253519359d8a063d3bafad8d","analyzedAt":"2026-08-12T22:18:56.877Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}