nexu-io/open-design · error · Error
no Leonardo.ai API key — configure it in Settings or set LEO
Error message
no Leonardo.ai API key — configure it in Settings or set LEONARDO_API_KEY
What it means
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.
Source
Thrown at apps/daemon/src/media/index.ts:2175
function openRouterAspectFor(aspect?: string): string {
// OpenRouter normalises aspect ratios across providers. Our
// MEDIA_ASPECTS vocabulary is a strict subset — pass known values
// through, default to 16:9 for video.
if (
aspect === '1:1'
|| aspect === '16:9'
|| aspect === '9:16'
|| aspect === '4:3'
|| aspect === '3:4'
) {
return aspect;
}
return '16:9';
}
async function renderLeonardoImage(ctx: MediaContext, credentials: ProviderConfig): Promise<RenderResult> {
if (!credentials.apiKey) {
throw new Error(
'no Leonardo.ai API key — configure it in Settings or set LEONARDO_API_KEY',
);
}
const baseUrl = (credentials.baseUrl || 'https://cloud.leonardo.ai/api/rest/v1').replace(/\/$/, '');
// Map model IDs to Leonardo.ai platform model IDs
const modelMap: Record<string, string> = {
'leonardo-phoenix': '6b645e3a-d64f-4341-a6d8-7a3690fbf042', // Phoenix
'leonardo-kino-xl': 'aa77f04e-3eec-4034-9c07-d0f619684628', // Kino XL
'leonardo-flux-dev': 'b2614463-296c-462a-9586-aafdb8f00e36', // FLUX.1 [dev]
'leonardo-flux-schnell': '1dd50843-d653-4516-a8e3-f0238ee453ff', // FLUX.1 [schnell]
'leonardo-anime-pastel': '1e60896f-3c26-4296-8ecc-53e2afecc132', // Anime Pastel Dream
};
const platformModelId = modelMap[ctx.model];
if (!platformModelId) {
throw new Error(`unsupported leonardo.ai model: ${ctx.model}`);
}View on GitHub (pinned to 5be4028344)
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).
Example fix
// before — daemon started without the key $ pnpm tools-dev # → renderLeonardoImage throws [462] // after $ export LEONARDO_API_KEY=$(cat ~/.secrets/leonardo.key) $ pnpm tools-dev
Defensive patterns
Strategy: validation
Validate before calling
// Validate credential presence before dispatching to the Leonardo renderer.
function assertLeonardoCreds(credentials: ProviderConfig): void {
if (!credentials.apiKey || typeof credentials.apiKey !== 'string') {
throw new Error(
'no Leonardo.ai API key — configure it in Settings or set LEONARDO_API_KEY',
);
}
} Type guard
function hasLeonardoCredential(c: ProviderConfig): c is ProviderConfig & { apiKey: string } {
return typeof c?.apiKey === 'string' && c.apiKey.length > 0;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- no Nano Banana API key — configure it in Settings or set OD_
- nano-banana image ${resp.status}: ${truncate(text, 240)}
- no OpenRouter API key — configure it in Settings or set OPEN
- openrouter image ${resp.status}: ${truncate(text, 240)}
- unsupported leonardo.ai model: ${ctx.model}
AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12).
Data as JSON: /api/errors/540fd28dba53658c.
Report an issue: GitHub.