nexu-io/open-design · error · Error
leonardo.ai submit ${submitResp.status}: ${truncate(submitTe
Error message
leonardo.ai submit ${submitResp.status}: ${truncate(submitText, 240)} What it means
Thrown when the POST to {baseUrl}/generations on Leonardo.ai returned a non-2xx status. The message includes the HTTP status and the first 240 chars of the response body (truncated via truncate()) so the operator can see Leonardo's error text. This is the upstream-rejection guard for the submit step, distinct from JSON-parse and shape errors that follow.
Source
Thrown at apps/daemon/src/media/index.ts:2234
modelId: platformModelId,
width: size.width,
height: size.height,
num_images: 1,
...(requiresContrast ? { contrast: 3.5 } : {}),
};
const submitResp = await fetch(`${baseUrl}/generations`, withMediaRequestInit(ctx, {
method: 'POST',
headers: {
'authorization': `Bearer ${credentials.apiKey}`,
'content-type': 'application/json',
},
body: JSON.stringify(body),
}));
const submitText = await submitResp.text();
if (!submitResp.ok) {
throw new Error(`leonardo.ai submit ${submitResp.status}: ${truncate(submitText, 240)}`);
}
let submitData: any;
try {
submitData = JSON.parse(submitText);
} catch {
throw new Error(`leonardo.ai non-JSON: ${truncate(submitText, 200)}`);
}
const generationId = submitData?.sdGenerationJob?.generationId;
if (!generationId) {
throw new Error('leonardo.ai response missing generationId');
}
// Poll for completion
const maxPollMs = 120000; // 2 minutes
const pollIntervalMs = 2000; // 2 seconds
const startedAt = Date.now();View on GitHub (pinned to 5be4028344)
Solutions
- Read the truncated body in the error message — Leonardo usually states the exact reason (out_of_credits, invalid_model, unauthorized).
- If 401/403: rotate the LEONARDO_API_KEY (Settings or env) and retry.
- If 402/429: check your Leonardo plan/credit balance at cloud.leonardo.ai and top up or wait for quota reset.
- If 400: verify the model+aspect combination is supported — some models restrict dimensions; try 1:1 first to isolate.
- If 5xx: retry after a short backoff; check status.leonardo.ai.
Example fix
// before — model+aspect combo Leonardo rejects
body = { modelId: '...', width: 1344, height: 768 } // → HTTP 400
// after
body = { modelId: '...', width: 1024, height: 1024 } // supported 1:1 Defensive patterns
Strategy: try-catch
Validate before calling
// Pre-validate the request shape against Leonardo's known constraints before
// spending a network round-trip on a guaranteed 400.
function validateLeonardoSubmitBody(body: Record<string, unknown>, model: string): string[] {
const errors: string[] = [];
if (!body.modelId) errors.push('modelId required');
if (typeof body.width !== 'number' || typeof body.height !== 'number') errors.push('width/height required');
if ((body.width as number) > 1024 && model === 'leonardo-flux-schnell') errors.push('flux-schnell max 1024 on long edge');
return errors;
} Try / catch
// Classify the submit failure so callers can retry only idempotent classes.
try {
await renderLeonardoImage(ctx, creds);
} catch (e) {
const msg = String((e as Error).message || e);
const m = msg.match(/leonardo\.ai submit (\d+)/);
if (m) {
const status = Number(m[1]);
if (status === 429 || status >= 500) { /* transient — safe to retry */ await renderLeonardoImage(ctx, creds); return; }
if (status === 401 || status === 403) throw new Error('Leonardo API key invalid — reconfigure in Settings');
if (status === 402) throw new Error('Leonardo credits exhausted — top up at cloud.leonardo.ai');
}
throw e;
} Prevention
- Test new model+aspect combinations in the Leonardo playground before wiring them into production prompts.
- Monitor Leonardo credit balance via their API on a schedule and alert before exhaustion.
- Keep the API key in a rotation pipeline so 401s surface during planned rotation, not mid-job.
When it happens
Trigger: Leonardo.ai rejects the generation request: invalid/expired API key (401), quota/plan exhausted (402/429), malformed body or unsupported model/dimension combination (400), or Leonardo-side outage (5xx).
Common situations: Free-tier plan out of credits; API key revoked; combination of model + dimensions Leonardo rejects (e.g. requesting a size the chosen model doesn't support); transient 5xx during Leonardo maintenance; clock/auth skew after rotating keys.
Related errors
- grok video submit ${submitResp.status}: ${truncate(submitTex
- leonardo.ai poll ${pollResp.status}
- leonardo.ai image fetch ${imgResp.status}
- public_github_repository_metric request failed with ${respon
- ${tag} ${resp.status}: ${truncate(text, 240)}
AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12).
Data as JSON: /api/errors/6fbb8f991a3b8f8e.
Report an issue: GitHub.