{"record":{"id":"966741dbd411dc1b","repo":"heygen-com/hyperframes","slug":"render-failed-966741","errorCode":"RENDER_FAILED","errorMessage":"figma could not render node ${nodeId} as ${opts.format}","messagePattern":"figma could not render node (.+?) as (.+?)","errorType":"error_code","errorClass":"FigmaClientError","httpStatus":null,"severity":"error","filePath":"packages/core/src/figma/client.ts","lineNumber":315,"sourceCode":"    // per-minute, so a couple of imports in quick succession hit it and a\n    // short wait clears it. Honor Retry-After when present, else exponential.\n    let res: Response;\n    for (let attempt = 0; ; attempt += 1) {\n      res = await doFetch(`${base}${path}`, { headers: { \"X-Figma-Token\": token } });\n      if (res.status !== 429 || attempt >= maxRetries) break;\n      const wait = retryAfterMs(res) ?? 1000 * 2 ** attempt;\n      await sleep(wait);\n    }\n    await throwForStatus(res, path, opts);\n    return res.json();\n  }\n\n  return {\n    async renderNode(ref, opts) {\n      const nodeId = requireNodeId(ref);\n      const [result] = await this.renderNodes(ref.fileKey, [nodeId], opts);\n      if (!result || result.url === null)\n        throw new FigmaClientError(\n          \"RENDER_FAILED\",\n          `figma could not render node ${nodeId} as ${opts.format}`,\n          undefined,\n          \"images\",\n        );\n      return { url: result.url, ext: opts.format };\n    },\n\n    async renderNodes(fileKey, nodeIds, opts) {\n      if (nodeIds.length === 0) return [];\n      // /v1/images accepts comma-separated ids — one call for the whole batch,\n      // which is figma's own answer to the per-minute rate limit.\n      const params = new URLSearchParams({ ids: nodeIds.join(\",\"), format: opts.format });\n      if (opts.scale !== undefined) params.set(\"scale\", String(opts.scale));\n      const body = await get(`/v1/images/${fileKey}?${params}`, {\n        scopeHint: SCOPE_HINTS.fileContent,\n        endpoint: \"images\",\n      });","sourceCodeStart":297,"sourceCodeEnd":333,"githubUrl":"https://github.com/heygen-com/hyperframes/blob/c2996c8626135db5253519359d8a063d3bafad8d/packages/core/src/figma/client.ts#L297-L333","documentation":"Thrown by renderNode (code RENDER_FAILED) when the single-node render returns either no result or a result whose url is null. renderNode is a thin wrapper over renderNodes (the batch endpoint /v1/images) — batch calls intentionally return url:null for individual nodes that figma could not render so one bad node doesn't fail the whole batch, but the single-node renderNode has no such tolerance and escalates a null url to an exception. Common when the node id is valid enough to be accepted by the API but figma cannot rasterise it (e.g. an empty frame, a component set, a locked node).","triggerScenarios":"Calling renderNode with a nodeId pointing at an empty/zero-size frame; rendering a node type figma refuses to rasterise (e.g. a Component Set or a Boolean Operation container); the node was deleted between parsing and rendering; an internal figma render error surfaced as a null url rather than an HTTP status.","commonSituations":"Copying a node id from the figma URL for a container/group that has no visual content; rendering a slice or guide node; the figma file's render backend timing out for one specific heavy frame.","solutions":["Open the node in figma and confirm it has visible, renderable content (not an empty frame or a component-set container).","Render a child node with actual fills/children instead of the parent container.","Try a different format (svg vs png) — some node types render in one but not the other.","If you need to tolerate per-node failures, call renderNodes directly and skip entries with url === null."],"exampleFix":"// before — single-node call throws if figma returns url:null\nconst { url } = await client.renderNode(ref, { format: 'png' });\n\n// after — batch call lets you skip unrenderable nodes\nconst [r] = await client.renderNodes(ref.fileKey, [ref.nodeId!], { format: 'png' });\nif (!r || r.url === null) continue; // tolerate per-node failure","handlingStrategy":"fallback","validationCode":null,"typeGuard":"import { FigmaClientError } from '.../figma/client';\nexport function isRenderFailed(err: unknown): err is FigmaClientError {\n  return err instanceof FigmaClientError && err.code === 'RENDER_FAILED';\n}","tryCatchPattern":"// Fall back from renderNode to a tolerant batch call, skipping unrenderable nodes\ntry {\n  return await client.renderNode(ref, opts);\n} catch (err) {\n  if (isRenderFailed(err)) {\n    const [r] = await client.renderNodes(ref.fileKey, [ref.nodeId!], opts);\n    if (r && r.url !== null) return { url: r.url, ext: opts.format };\n    // give up gracefully on this node\n    return null;\n  }\n  throw err;\n}","preventionTips":["Prefer renderNodes over renderNode when importing many assets so per-node failures don't abort the batch.","Validate the node has visible content in figma before exporting.","If a node persistently fails, try a different format (svg vs png)."],"tags":["figma","render","api","images"],"backgroundTag":null,"analyzedSha":"c2996c8626135db5253519359d8a063d3bafad8d","analyzedAt":"2026-08-12T22:18:56.877Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}