heygen-com/hyperframes · warning · FigmaClientError
RATE_LIMITED
RATE_LIMITED
Error message
figma rate limit hit (429) and still limited after ${maxRetries} retries — wait a minute and re-run, or import fewer nodes per call. What it means
Thrown by throwForStatus (code RATE_LIMITED, status 429) only AFTER the get() retry loop has already attempted up to maxRetries (default 3) requests with backoff — honoring Retry-After when figma sends it, else exponential 1s/2s/4s. Reaching this throw means the per-minute rate budget was still exhausted after those waits. The client deliberately surfaces it rather than blocking silently because, past a few retries, the user is better off reducing batch size or waiting a minute than watching the CLI hang.
Source
Thrown at packages/core/src/figma/client.ts:281
`figma denied access (403). ${scopeLine} Also confirm the file is visible to your account.`,
403,
opts.endpoint,
);
}
/** Throw the typed error for a non-ok response (no-op when res.ok). */
async function throwForStatus(res: Response, path: string, opts: GetOptions): Promise<void> {
if (res.ok) return;
if (res.status === 401)
throw new FigmaClientError(
"BAD_TOKEN",
"figma rejected the token (401) — it is expired or revoked. Re-mint at figma.com/settings → Security, then update FIGMA_TOKEN.",
401,
opts.endpoint,
);
if (res.status === 403) throw forbiddenError(await readFigmaErrorMessage(res), opts);
if (res.status === 429)
throw new FigmaClientError(
"RATE_LIMITED",
`figma rate limit hit (429) and still limited after ${maxRetries} retries — wait a minute and re-run, or import fewer nodes per call.`,
429,
opts.endpoint,
);
throw new FigmaClientError(
"HTTP_ERROR",
`figma request failed: HTTP ${res.status} ${path}`,
res.status,
opts.endpoint,
);
}
async function get(path: string, opts: GetOptions): Promise<unknown> {
// Retry 429 with backoff before surfacing RATE_LIMITED — figma's limit is
// per-minute, so a couple of imports in quick succession hit it and a
// short wait clears it. Honor Retry-After when present, else exponential.
let res: Response;View on GitHub (pinned to c2996c8626)
Solutions
- Wait ~60 seconds and re-run — figma's limit is per-minute and a single wait usually clears it.
- Reduce the number of nodes per renderNodes call, or space successive calls with a small delay.
- Batch node renders into a single renderNodes call (comma-separated ids) rather than many renderNode calls — this is figma's own recommended workaround.
- If it happens routinely, raise maxRetries or pass a custom sleep to spread retries further.
Example fix
// before — many single-node calls hammer the per-minute limit
for (const id of nodeIds) {
await client.renderNode({ fileKey, nodeId: id }, { format: 'png' });
}
// after — one batched call, figma's documented rate-limit workaround
await client.renderNodes(fileKey, nodeIds, { format: 'png' }); Defensive patterns
Strategy: retry
Type guard
import { FigmaClientError } from '.../figma/client';
export function isRateLimited(err: unknown): err is FigmaClientError {
return err instanceof FigmaClientError && err.code === 'RATE_LIMITED';
} Try / catch
async function withRateLimitRetry<T>(fn: () => Promise<T>): Promise<T> {
for (let attempt = 0; ; attempt++) {
try { return await fn(); }
catch (err) {
if (isRateLimited(err) && attempt < 2) {
await new Promise(r => setTimeout(r, 60_000)); // figma limit is per-minute
continue;
}
throw err;
}
}
} Prevention
- Batch node renders into a single renderNodes call (figma's documented workaround).
- Throttle concurrent figma calls with a per-minute token bucket in your importer.
- Tune maxRetries and the injected sleep in tests (no-op sleep) so unit tests don't block on retry.
When it happens
Trigger: Issuing many renderNodes/nodeTree calls in a tight loop within the same minute; a single batched renderNodes call with a very large nodeIds list; figma tier-level quota exhaustion (Retry-After in the thousands of seconds, capped at MAX_RETRY_WAIT_MS=60s by retryAfterMs); running multiple CLI processes against the same token concurrently.
Common situations: Bulk-importing dozens of figma frames in one script run; CI matrix jobs sharing a token; a long export session that creeps over the per-minute limit near the end; the free figma tier's lower rate ceiling.
Related errors
- figma render download failed: HTTP ${res.status}
- OpenRouter request failed with HTTP ${res.status}
- figma render returned non-SVG bytes for an svg export — retr
- ${failureStage}${attemptDetail}: ${describeFetchFailure(last
- HTTP_ERROR
AI-assisted analysis of heygen-com/hyperframes@c2996c8626 (2026-08-12).
Data as JSON: /api/errors/ed0b8a4af62a5e06.
Report an issue: GitHub.