{"record":{"id":"2344a4d52c76c59b","repo":"nexu-io/open-design","slug":"leonardo-ai-generation-failed","errorCode":null,"errorMessage":"leonardo.ai generation failed","messagePattern":"leonardo\\.ai generation failed","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/daemon/src/media/index.ts","lineNumber":2278,"sourceCode":"        'authorization': `Bearer ${credentials.apiKey}`,\n      },\n    }));\n    \n    if (!pollResp.ok) {\n      throw new Error(`leonardo.ai poll ${pollResp.status}`);\n    }\n    \n    const pollData = (await pollResp.json()) as Record<string, any>;\n    const generation = pollData?.generations_by_pk;\n    \n    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`,","sourceCodeStart":2260,"sourceCodeEnd":2296,"githubUrl":"https://github.com/nexu-io/open-design/blob/5be4028344c2eb4c667c5a97bda8f750c5597ef7/apps/daemon/src/media/index.ts#L2260-L2296","documentation":"Thrown when a Leonardo poll returns generation.status === 'FAILED'. This is the upstream terminal-failure signal — Leonardo accepted the job and then internally failed it (content-policy, model error, moderation). Distinct from [467] (transport failure) and [469] (no answer in time): this is a definitive negative answer.","triggerScenarios":"Polling loop receives 200 with { generations_by_pk: { status: 'FAILED' } }. Commonly caused by prompts that trip Leonardo's content filters, unsupported parameter combinations, or upstream model crashes.","commonSituations":"Prompt contains content Leonardo's moderation blocks; requested model + size + guidance combination Leonardo's backend can't satisfy; transient Leonardo model crash; safety filter false-positive on benign prompt.","solutions":["Rephrase the prompt to avoid content that may trip moderation; remove explicit/sensitive terms and retry.","Try a different Leonardo model (e.g. switch from Phoenix to FLUX) — some failures are model-specific.","Retry once — model crashes and intermittent content-filter false positives often succeed on a second attempt.","If failures are persistent and the prompt is benign, file a Leonardo support ticket with the generationId."],"exampleFix":"// before\nif (generation?.status === 'FAILED') {\n  throw new Error('leonardo.ai generation failed');\n}\n\n// after — surface upstream reason if present\nif (generation?.status === 'FAILED') {\n  const reason = generation?.error_message || generation?.failure_reason || 'no reason returned';\n  throw new Error(`leonardo.ai generation failed: ${reason}`);\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"// Distinguish content-policy FAILED from transient model FAILED so the caller\n// can retry the latter but not the former.\nif (generation?.status === 'FAILED') {\n  const reason = String(generation?.error_message || generation?.failure_reason || '').toLowerCase();\n  const err = new Error(`leonardo.ai generation failed: ${reason || 'no reason returned'}`);\n  (err as any).retryable = /timeout|temporary|server|unknown/.test(reason);\n  throw err;\n}","preventionTips":["Surface generation.error_message in the thrown error so users can tell moderation from model crashes — the current fixed string forces them to file a ticket to learn why.","Run a moderation pre-check on the prompt for production pipelines that can't afford a FAILED after 2 minutes of polling.","Track FAILED rate per model in observability; a spike usually means an upstream regression or a content-policy boundary shift."],"tags":["leonardo","upstream-failure","moderation","terminal-status"],"backgroundTag":null,"analyzedSha":"5be4028344c2eb4c667c5a97bda8f750c5597ef7","analyzedAt":"2026-08-12T12:03:58.812Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}