nexu-io/open-design · error · Error

leonardo.ai response missing generationId

Error message

leonardo.ai response missing generationId

What it means

Thrown when the Leonardo submit response parsed as JSON but the expected field submitData.sdGenerationJob.generationId is missing/falsy. Leonardo's documented contract is { sdGenerationJob: { generationId } }; a 2xx response without that path means the API shape changed or an unexpected success envelope was returned. The message is a fixed string (no interpolated values), so debugging requires capturing the raw response.

Source

Thrown at apps/daemon/src/media/index.ts:2246

    },
    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();
  let imageUrl: string | null = null;
  
  while (Date.now() - startedAt < maxPollMs) {
    await new Promise(resolve => setTimeout(resolve, pollIntervalMs));
    
    const pollResp = await fetch(`${baseUrl}/generations/${generationId}`, withMediaRequestInit(ctx, {
      headers: {
        'authorization': `Bearer ${credentials.apiKey}`,
      },
    }));
    
    if (!pollResp.ok) {

View on GitHub (pinned to 5be4028344)

Solutions

  1. Reproduce the call with curl against {baseUrl}/generations to inspect the actual response envelope and compare to Leonardo's current API docs.
  2. If Leonardo changed the shape, update the extraction path in apps/daemon/src/media/index.ts (line ~2246) to match the new contract.
  3. Confirm the configured baseUrl matches the Leonardo REST API version documented for your account; an older/newer version can return a different envelope.
  4. If the response is genuinely malformed, file a Leonardo support ticket with the request body.

Example fix

// before
const generationId = submitData?.sdGenerationJob?.generationId;
if (!generationId) {
  throw new Error('leonardo.ai response missing generationId');
}

// after — surface body for diagnosis + accept alternate shapes
const generationId =
  submitData?.sdGenerationJob?.generationId
  ?? submitData?.generationId
  ?? submitData?.id;
if (!generationId) {
  throw new Error(`leonardo.ai response missing generationId: ${truncate(JSON.stringify(submitData), 240)}`);
}
Defensive patterns

Strategy: type-guard

Type guard

// Narrow the parsed Leonardo submit response so missing generationId is caught
// at the boundary with a typed error instead of a stringy runtime throw.
interface LeonardoSubmitOk { sdGenerationJob: { generationId: string } }
function isLeonardoSubmitOk(d: unknown): d is LeonardoSubmitOk {
  return !!d
    && typeof d === 'object'
    && typeof (d as any)?.sdGenerationJob?.generationId === 'string'
    && (d as any).sdGenerationJob.generationId.length > 0;
}

Try / catch

let submitData: any;
try { submitData = JSON.parse(submitText); } catch { throw new Error(`leonardo.ai non-JSON: ${truncate(submitText, 200)}`); }
if (!isLeonardoSubmitOk(submitData)) {
  // Surface the body so the next dev can see what shape Leonardo actually returned.
  throw new Error(`leonardo.ai response missing generationId: ${truncate(JSON.stringify(submitData), 240)}`);
}
const generationId = submitData.sdGenerationJob.generationId;

Prevention

When it happens

Trigger: Leonardo returns 2xx JSON without sdGenerationJob.generationId — e.g. a different envelope ({ generationId } at root, or { sdGenerationJob: { generationId: null } }), or an empty success object. The current code can't surface the body in the message.

Common situations: Leonardo ships an API version bump that changes the response shape; the configured baseUrl points at a different Leonardo API version; Leonardo returns a soft-success that is actually an error (e.g. validation passed-through as 200).

Related errors


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