{"record":{"id":"a9aeb9032395e47a","repo":"nexu-io/open-design","slug":"leonardo-ai-image-fetch-imgresp-status","errorCode":null,"errorMessage":"leonardo.ai image fetch ${imgResp.status}","messagePattern":"leonardo\\.ai image fetch (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/daemon/src/media/index.ts","lineNumber":2289,"sourceCode":"    if (generation?.status === 'COMPLETE') {\n      const images = generation?.generated_images;\n      if (Array.isArray(images) && images.length > 0) {\n        imageUrl = images[0]?.url;\n        break;\n      }\n    } else if (generation?.status === 'FAILED') {\n      throw new Error('leonardo.ai generation failed');\n    }\n  }\n  \n  if (!imageUrl) {\n    throw new Error('leonardo.ai generation timed out after 2 minutes');\n  }\n  \n  // Fetch the generated image\n  const imgResp = await fetch(imageUrl, withMediaRequestInit(ctx));\n  if (!imgResp.ok) {\n    throw new Error(`leonardo.ai image fetch ${imgResp.status}`);\n  }\n  \n  const bytes = Buffer.from(await imgResp.arrayBuffer());\n  \n  return {\n    bytes,\n    providerNote: `leonardo.ai/${ctx.model} · ${ctx.aspect} · ${bytes.length} bytes`,\n    suggestedExt: sniffImageExt(bytes),\n  };\n}\n\n\nasync function renderGrokVideo(ctx: MediaContext, credentials: ProviderConfig, onProgress?: ProgressFn): Promise<RenderResult> {\n  if (!credentials.apiKey) {\n    throw new Error(\n      'no xAI credentials — sign in with your SuperGrok subscription (in OD or via `hermes auth add xai-oauth`), set XAI_API_KEY, or configure a key in Settings',\n    );\n  }","sourceCodeStart":2271,"sourceCodeEnd":2307,"githubUrl":"https://github.com/nexu-io/open-design/blob/5be4028344c2eb4c667c5a97bda8f750c5597ef7/apps/daemon/src/media/index.ts#L2271-L2307","documentation":"Thrown after a successful generation when the GET to download the generated image bytes from imageUrl returned a non-2xx status. The image URL comes from generation.generated_images[0].url (a Leonardo CDN link). The message reports the raw HTTP status so you can distinguish auth (401/403) from gone (404/410) from CDN 5xx.","triggerScenarios":"Generation completed, imageUrl extracted, but fetch(imageUrl, withMediaRequestInit(ctx)) returns imgResp.ok === false. Happens when the Leonardo CDN link expired between completion and download, when CDN has a transient 5xx, or when egress to the CDN host is blocked.","commonSituations":"Daemon paused (e.g. on queue/backpressure) long enough that the signed CDN URL expired; Leonardo CDN maintenance (5xx); firewall blocking the image CDN host; transient CDN 503/504.","solutions":["Retry the render — expired signed URLs and CDN 5xx usually resolve on a second attempt.","If 404/410: the generated image was purged before download — re-run the generation.","Verify daemon host can reach the Leonardo image CDN (network egress rules).","If persistent, file a Leonardo support ticket — image lost between generation and storage is an upstream defect."],"exampleFix":"// before\nconst imgResp = await fetch(imageUrl, withMediaRequestInit(ctx));\nif (!imgResp.ok) {\n  throw new Error(`leonardo.ai image fetch ${imgResp.status}`);\n}\n\n// after — retry transient failures before throwing\nasync function fetchImageBytes(url: string, attempts = 3): Promise<Buffer> {\n  for (let i = 0; i < attempts; i++) {\n    const r = await fetch(url);\n    if (r.ok) return Buffer.from(await r.arrayBuffer());\n    if (r.status >= 500 && i < attempts - 1) { await new Promise(res => setTimeout(res, 1500 * (i + 1))); continue; }\n    throw new Error(`leonardo.ai image fetch ${r.status}`);\n  }\n  throw new Error('leonardo.ai image fetch exhausted retries');\n}","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"// Image fetch failures are dominated by transient CDN 5xx and expired signed\n// URLs — retry with backoff, bail on 4xx that won't self-heal.\nasync function fetchLeonardoImage(url: string, attempts = 3): Promise<Buffer> {\n  let last: Error | null = null;\n  for (let i = 0; i < attempts; i++) {\n    const r = await fetch(url);\n    if (r.ok) return Buffer.from(await r.arrayBuffer());\n    last = new Error(`leonardo.ai image fetch ${r.status}`);\n    if (r.status >= 500 && i < attempts - 1) { await new Promise(res => setTimeout(res, 1500 * (i + 1))); continue; }\n    break;\n  }\n  throw last!;\n}","preventionTips":["Download image bytes immediately on COMPLETE — do not enqueue or pause between poll completion and fetch, signed URLs expire.","Whitelist Leonardo's image CDN host in daemon egress rules, not just the API host.","Log the imageUrl host on fetch failure to correlate which CDN region is failing."],"tags":["leonardo","download","http-status","cdn"],"backgroundTag":null,"analyzedSha":"5be4028344c2eb4c667c5a97bda8f750c5597ef7","analyzedAt":"2026-08-12T12:03:58.812Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}