{"record":{"id":"540fd28dba53658c","repo":"nexu-io/open-design","slug":"no-leonardo-ai-api-key-configure-it-in-settings","errorCode":null,"errorMessage":"no Leonardo.ai API key — configure it in Settings or set LEONARDO_API_KEY","messagePattern":"no Leonardo\\.ai API key — configure it in Settings or set LEONARDO_API_KEY","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/daemon/src/media/index.ts","lineNumber":2175,"sourceCode":"function openRouterAspectFor(aspect?: string): string {\n  // OpenRouter normalises aspect ratios across providers. Our\n  // MEDIA_ASPECTS vocabulary is a strict subset — pass known values\n  // through, default to 16:9 for video.\n  if (\n    aspect === '1:1'\n    || aspect === '16:9'\n    || aspect === '9:16'\n    || aspect === '4:3'\n    || aspect === '3:4'\n  ) {\n    return aspect;\n  }\n  return '16:9';\n}\n\nasync function renderLeonardoImage(ctx: MediaContext, credentials: ProviderConfig): Promise<RenderResult> {\n  if (!credentials.apiKey) {\n    throw new Error(\n      'no Leonardo.ai API key — configure it in Settings or set LEONARDO_API_KEY',\n    );\n  }\n  const baseUrl = (credentials.baseUrl || 'https://cloud.leonardo.ai/api/rest/v1').replace(/\\/$/, '');\n  \n  // Map model IDs to Leonardo.ai platform model IDs\n  const modelMap: Record<string, string> = {\n    'leonardo-phoenix': '6b645e3a-d64f-4341-a6d8-7a3690fbf042',  // Phoenix\n    'leonardo-kino-xl': 'aa77f04e-3eec-4034-9c07-d0f619684628',  // Kino XL\n    'leonardo-flux-dev': 'b2614463-296c-462a-9586-aafdb8f00e36', // FLUX.1 [dev]\n    'leonardo-flux-schnell': '1dd50843-d653-4516-a8e3-f0238ee453ff', // FLUX.1 [schnell]\n    'leonardo-anime-pastel': '1e60896f-3c26-4296-8ecc-53e2afecc132', // Anime Pastel Dream\n  };\n  \n  const platformModelId = modelMap[ctx.model];\n  if (!platformModelId) {\n    throw new Error(`unsupported leonardo.ai model: ${ctx.model}`);\n  }","sourceCodeStart":2157,"sourceCodeEnd":2193,"githubUrl":"https://github.com/nexu-io/open-design/blob/5be4028344c2eb4c667c5a97bda8f750c5597ef7/apps/daemon/src/media/index.ts#L2157-L2193","documentation":"Thrown at the top of renderLeonardoImage when credentials.apiKey is falsy. The Leonardo.ai provider requires a bearer token; the daemon did not find one in the resolved ProviderConfig. The message lists all valid routes (Settings UI, LEONARDO_API_KEY env var) so the user knows the three ways to supply it.","triggerScenarios":"User invokes image generation on a leonardo-* model while no Leonardo credential is configured in Settings and no LEONARDO_API_KEY environment variable is set on the daemon process.","commonSituations":"First-time use of a Leonardo model without setup; env var set on a different shell than the daemon was started from; key cleared/removed from Settings but model selection still defaults to Leonardo; deploy of a fresh daemon without migrating the credentials store.","solutions":["Open Settings in the web UI, find the Leonardo.ai provider, paste the API key, and save.","Or set LEONARDO_API_KEY in the daemon's environment and restart: export LEONARDO_API_KEY=<key> (then restart the daemon so it inherits the var).","Verify the key is valid at cloud.leonardo.ai before configuring — an expired token will pass this guard but fail later at submit.","Confirm the daemon process actually inherited the env var (ps e <pid> | grep LEONARDO or check tools-dev logs)."],"exampleFix":"// before — daemon started without the key\n$ pnpm tools-dev\n# → renderLeonardoImage throws [462]\n\n// after\n$ export LEONARDO_API_KEY=$(cat ~/.secrets/leonardo.key)\n$ pnpm tools-dev","handlingStrategy":"validation","validationCode":"// Validate credential presence before dispatching to the Leonardo renderer.\nfunction assertLeonardoCreds(credentials: ProviderConfig): void {\n  if (!credentials.apiKey || typeof credentials.apiKey !== 'string') {\n    throw new Error(\n      'no Leonardo.ai API key — configure it in Settings or set LEONARDO_API_KEY',\n    );\n  }\n}","typeGuard":"function hasLeonardoCredential(c: ProviderConfig): c is ProviderConfig & { apiKey: string } {\n  return typeof c?.apiKey === 'string' && c.apiKey.length > 0;\n}","tryCatchPattern":null,"preventionTips":["Run a credential preflight on daemon startup that asserts every provider the user has selected has a non-empty key; fail loudly at boot rather than at first render.","When prompting the user to pick a model, surface a 'not configured' badge on Leonardo models if the credential is missing so they don't get a runtime error.","Store credentials in the daemon data root and validate them on save (a quick GET to Leonardo's user endpoint) so misconfigured keys fail at Settings time."],"tags":["leonardo","credentials","configuration","image-generation"],"backgroundTag":null,"analyzedSha":"5be4028344c2eb4c667c5a97bda8f750c5597ef7","analyzedAt":"2026-08-12T12:03:58.812Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}