{"record":{"id":"d65b3e4197453a97","repo":"Egonex-AI/Understand-Anything","slug":"figma-api-path-failed-res-status-res-stat","errorCode":null,"errorMessage":"Figma API ${path} failed: ${res.status} ${res.statusText}","messagePattern":"Figma API (.+?) failed: (.+?) (.+?)","errorType":"http","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"understand-anything-plugin/packages/core/src/figma/source/api-source.ts","lineNumber":29,"sourceCode":"\nexport class FigmaApiSource implements FigmaSource {\n  private readonly token: string;\n\n  constructor(private readonly fileKey: string, token: string | undefined = process.env.FIGMA_TOKEN) {\n    if (!token) {\n      throw new Error(\n        \"FIGMA_TOKEN is not set. Create a personal access token at \" +\n        \"https://www.figma.com/settings, then run: export FIGMA_TOKEN=<token>\",\n      );\n    }\n    this.token = token;\n  }\n\n  private async get<T>(path: string): Promise<T> {\n    // Token travels only in the header — never in the URL, never logged.\n    const res = await fetch(`${FIGMA_API}${path}`, { headers: { \"X-Figma-Token\": this.token } });\n    if (!res.ok) {\n      throw new Error(`Figma API ${path} failed: ${res.status} ${res.statusText}`);\n    }\n    return (await res.json()) as T;\n  }\n\n  fetchDocument(): Promise<FigmaDocument> {\n    return this.get<FigmaDocument>(`/files/${this.fileKey}`);\n  }\n\n  fetchStyles(): Promise<FigmaStyles> {\n    return this.get<FigmaStyles>(`/files/${this.fileKey}/styles`);\n  }\n\n  async renderImages(nodeIds: string[]): Promise<Record<string, string>> {\n    if (nodeIds.length === 0) return {};\n    const ids = encodeURIComponent(nodeIds.join(\",\"));\n    const data = await this.get<{ images: Record<string, string> }>(\n      `/images/${this.fileKey}?ids=${ids}&format=png&scale=1`,\n    );","sourceCodeStart":11,"sourceCodeEnd":47,"githubUrl":"https://github.com/Egonex-AI/Understand-Anything/blob/32944829e7a63a9fa9c55d811d7f98a9530c6a6a/understand-anything-plugin/packages/core/src/figma/source/api-source.ts#L11-L47","documentation":"Thrown by the private get<T> helper inside FigmaApiSource whenever fetch resolves with a non-ok HTTP status. It reports the Figma API path plus the HTTP status code and status text, covering the fetchDocument, fetchStyles, and renderImages calls. No retry or response-body inspection is performed — any 4xx/5xx is fatal.","triggerScenarios":"A 401/403 when the token is wrong or lacks scope; a 404 when the fileKey does not exist or the token cannot access that file; a 429 when rate limited; a 5xx on a transient Figma outage; an empty nodeIds call never reaches here (short-circuited) but renderImages with bad IDs can.","commonSituations":"Token valid but lacking access to the target file; stale fileKey after a file was moved/deleted; hitting Figma rate limits under heavy use; transient API errors during a Figma incident; copy-pasting a file key from a different team's file.","solutions":["Inspect the HTTP status in the message: 401/403 means regenerate/refresh the token and verify file permissions; 404 means re-copy the fileKey from the Figma URL.","For 429, slow down and retry with exponential backoff honouring Figma's rate-limit headers.","For 5xx, retry after a short delay — these are transient on Figma's side.","Wrap the fetchDocument/fetchStyles/renderImages calls in a retry layer that re-reads the status and backs off only on 429/5xx."],"exampleFix":"// before\nconst doc = await src.fetchDocument();\n// after — surface and branch on the status\ntry { const doc = await src.fetchDocument(); }\ncatch (e) {\n  const m = String(e.message).match(/failed: (\\d+)/);\n  if (m && m[1] === '429') { /* back off and retry */ }\n  else throw e;\n}","handlingStrategy":"retry","validationCode":"// pre-check is not possible for a live network call; verify inputs instead\nif (!fileKey) throw new Error('fileKey required before fetching');","typeGuard":null,"tryCatchPattern":"async function fetchWithRetry<T>(fn: () => Promise<T>, retries = 3): Promise<T> {\n  for (let attempt = 0; ; attempt++) {\n    try { return await fn(); }\n    catch (e) {\n      const m = String((e as Error).message).match(/failed: (\\d+)/);\n      const status = m ? Number(m[1]) : 0;\n      const transient = status === 429 || status >= 500;\n      if (!transient || attempt >= retries) throw e;\n      await new Promise(r => setTimeout(r, 2 ** attempt * 500));\n    }\n  }\n}\nconst doc = await fetchWithRetry(() => src.fetchDocument());","preventionTips":["Verify the token has access to the target file before bulk operations.","Rate-limit renderImages calls (batch node IDs) to avoid 429s.","Treat 401/403/404 as non-retryable; only retry 429 and 5xx."],"tags":["figma","network","http","api"],"backgroundTag":null,"analyzedSha":"32944829e7a63a9fa9c55d811d7f98a9530c6a6a","analyzedAt":"2026-08-12T10:25:44.261Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}