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

  1. Read the truncated body in the error message — Leonardo usually states the exact reason (out_of_credits, invalid_model, unauthorized).
  2. If 401/403: rotate the LEONARDO_API_KEY (Settings or env) and retry.
  3. If 402/429: check your Leonardo plan/credit balance at cloud.leonardo.ai and top up or wait for quota reset.
  4. If 400: verify the model+aspect combination is supported — some models restrict dimensions; try 1:1 first to isolate.
  5. 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

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


AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12). Data as JSON: /api/errors/6fbb8f991a3b8f8e. Report an issue: GitHub.