heygen-com/hyperframes · error · FigmaClientError

BAD_TOKEN

BAD_TOKEN

Error message

figma rejected the token (401) — it is expired or revoked. Re-mint at figma.com/settings → Security, then update FIGMA_TOKEN.

What it means

Thrown by throwForStatus (code BAD_TOKEN, status 401) when figma's REST API returns HTTP 401 on a GET. A 401 means the X-Figma-Token was syntactically a token but figma no longer accepts it — it has expired or been revoked. The error points the user back to the PAT settings page to re-mint. (Note: figma also returns 403 with body 'Invalid token' for some bad-token cases on file endpoints — those are handled separately by forbiddenError at client.ts:237 and surface the same BAD_TOKEN code.)

Source

Thrown at packages/core/src/figma/client.ts:273

        403,
        opts.endpoint,
      );
    const scopeLine = opts.scopeHint
      ? `This endpoint needs the "${opts.scopeHint}" scope — add it at figma.com/settings → Security → Personal access tokens.`
      : "The token is missing a read scope, or your account can't view this file. Check File content: Read-only + File metadata: Read-only at figma.com/settings → Security.";
    return new FigmaClientError(
      "FORBIDDEN",
      `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,

View on GitHub (pinned to c2996c8626)

Solutions

  1. Open figma.com/settings -> Security -> Personal access tokens and generate a new token.
  2. Update FIGMA_TOKEN in your shell (export FIGMA_TOKEN=...) and in the project .env / CI secret.
  3. Re-run the command that failed — the new token takes effect on the next createFigmaClient call.
  4. If it persists, confirm the token's scopes include File content: Read-only.

Example fix

// before — stale token in env
export FIGMA_TOKEN="figd_old_revoked_value"

// after — freshly minted token
export FIGMA_TOKEN="figd_newly_generated_value"
Defensive patterns

Strategy: try-catch

Type guard

import { FigmaClientError } from '.../figma/client';
export function isBadToken(err: unknown): err is FigmaClientError {
  return err instanceof FigmaClientError && (err.code === 'BAD_TOKEN');
}

Try / catch

try {
  await client.nodeTree(ref);
} catch (err) {
  if (isBadToken(err)) {
    // prompt the user to re-mint the PAT, update FIGMA_TOKEN, retry once
  } else throw err;
}

Prevention

When it happens

Trigger: Any client call (renderNode, nodeTree, styles, variables, fileVersion, imageFills) after the PAT was deleted or expired in figma; using a token from a different figma account than the one that owns the file (can also 403); a token that was regenerated in the UI but FIGMA_TOKEN in the shell still holds the old value.

Common situations: Token expired (figma PATs can have expirations or be revoked); the user regenerated a token and forgot to update the .env/shell; token pasted with trailing whitespace stripped wrongly (less likely since createFigmaClient trims); switching figma accounts mid-project.

Related errors


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