nexu-io/open-design · error · Error
unsupported leonardo.ai model: ${ctx.model}
Error message
unsupported leonardo.ai model: ${ctx.model} What it means
Thrown by renderLeonardoImage when ctx.model is not a key in the static modelMap. The map holds four Leonardo platform model IDs (phoenix, kino-xl, flux-dev, flux-schnell, anime-pastel); any other model identifier reaches the `if (!platformModelId)` branch. This guards against pushing an unknown model ID to Leonardo's API which would 400 upstream.
Source
Thrown at apps/daemon/src/media/index.ts:2192
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}`);
}
// Map aspect ratios to Leonardo.ai dimensions
const aspectMap: Record<string, { width: number; height: number }> = {
'1:1': { width: 1024, height: 1024 },
'16:9': { width: 1344, height: 768 },
'9:16': { width: 768, height: 1344 },
'4:3': { width: 1152, height: 896 },
'3:4': { width: 896, height: 1152 },
};
const size = (ctx.aspect ? aspectMap[ctx.aspect] : undefined) || { width: 1024, height: 1024 };
// Submit generation request. Phoenix and the FLUX family require the
// `contrast` field per Leonardo's API reference; valid values are
// 3 (Low) / 3.5 (Medium) / 4 (High). Default to 3.5 so prompts that
// omit a contrast hint fall in the middle of the supported range.
const requiresContrast =View on GitHub (pinned to 5be4028344)
Solutions
- Use one of the supported model IDs: leonardo-phoenix, leonardo-kino-xl, leonardo-flux-dev, leonardo-flux-schnell, or leonardo-anime-pastel.
- If you need a newly released Leonardo model, extend the modelMap in apps/daemon/src/media/index.ts (around line 2185) and rebuild the daemon.
- Check for typos: the prefix is 'leonardo-' (lowercase, hyphen-separated), not 'leonardo_' or 'Leonardo-'.
- If invoking through a skill/template, inspect the rendered prompt to confirm which model string it actually sent.
Example fix
// before ctx.model = 'leonardo-flux' // not in the map → [463] // after ctx.model = 'leonardo-flux-dev' // valid map key
Defensive patterns
Strategy: validation
Validate before calling
// Validate the model id is supported before reaching the renderer.
const LEONARDO_SUPPORTED_MODELS = new Set([
'leonardo-phoenix',
'leonardo-kino-xl',
'leonardo-flux-dev',
'leonardo-flux-schnell',
'leonardo-anime-pastel',
]);
function assertLeonardoModelSupported(model: string): void {
if (!LEONARDO_SUPPORTED_MODELS.has(model)) {
throw new Error(
`unsupported leonardo.ai model: ${model}. Supported: ${[...LEONARDO_SUPPORTED_MODELS].join(', ')}`,
);
}
} Type guard
function isLeonardoSupportedModel(m: string): m is
| 'leonardo-phoenix'
| 'leonardo-kino-xl'
| 'leonardo-flux-dev'
| 'leonardo-flux-schnell'
| 'leonardo-anime-pastel' {
return LEONARDO_SUPPORTED_MODELS.has(m);
} Prevention
- Expose the supported Leonardo model list as a contract type so the UI can offer a picker instead of a free-text field.
- When Leonardo releases a new model, add it to the Set AND the modelMap in the same commit, with a release note.
- Gate skill/template model references through a lint pass that rejects unknown Leonardo model ids.
When it happens
Trigger: User (or a skill/template) passes a model string not in {'leonardo-phoenix','leonardo-kino-xl','leonardo-flux-dev','leonardo-flux-schnell','leonardo-anime-pastel'} to the Leonardo renderer — e.g. a typo, a deprecated ID, or a model the dispatcher routed incorrectly.
Common situations: Typo in a skill prompt or template that names the model; Leonardo renamed/added a model and the user expects the new ID before the map is updated; a legacy project file references an old model alias.
Related errors
- no Leonardo.ai API key — configure it in Settings or set LEO
- leonardo.ai non-JSON: ${truncate(submitText, 200)}
- leonardo.ai generation timed out after 2 minutes
- invalid JSON in ${filePath}: ${message}
- ${filePath} must contain a JSON object
AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12).
Data as JSON: /api/errors/8a12814137069573.
Report an issue: GitHub.