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

  1. Wait ~60 seconds and re-run — figma's limit is per-minute and a single wait usually clears it.
  2. Reduce the number of nodes per renderNodes call, or space successive calls with a small delay.
  3. Batch node renders into a single renderNodes call (comma-separated ids) rather than many renderNode calls — this is figma's own recommended workaround.
  4. 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

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


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