nexu-io/open-design · error · Error
nano-banana image response missing candidates[].content.part
Error message
nano-banana image response missing candidates[].content.parts[].inlineData.data
What it means
Thrown by inlineImageBytesFromGenerateContent when the parsed Gemini response has no candidate whose content.parts contain an `inlineData.data` base64 string. The helper walks candidates→content→parts→inlineData.data and only succeeds if it finds a non-empty string; an empty candidates array, a finishReason-filtered response, or a prompt-blocked response all reach this throw.
Source
Thrown at apps/daemon/src/media/index.ts:1769
|| aspect === '3:4'
) {
return aspect;
}
return '1:1';
}
function inlineImageBytesFromGenerateContent(data: any): Buffer {
const candidates = Array.isArray(data?.candidates) ? data.candidates : [];
for (const candidate of candidates) {
const parts = Array.isArray(candidate?.content?.parts) ? candidate.content.parts : [];
for (const part of parts) {
const inline = part?.inlineData;
if (typeof inline?.data === 'string' && inline.data) {
return Buffer.from(inline.data, 'base64');
}
}
}
throw new Error('nano-banana image response missing candidates[].content.parts[].inlineData.data');
}
function sniffImageExt(bytes: Buffer): string {
if (bytes.length >= 3 && bytes[0] === 0xff && bytes[1] === 0xd8 && bytes[2] === 0xff) {
return '.jpg';
}
if (
bytes.length >= 8
&& bytes[0] === 0x89 && bytes[1] === 0x50 && bytes[2] === 0x4e && bytes[3] === 0x47
) {
return '.png';
}
if (
bytes.length >= 12
&& bytes[0] === 0x52 && bytes[1] === 0x49 && bytes[2] === 0x46 && bytes[3] === 0x46
&& bytes[8] === 0x57 && bytes[9] === 0x45 && bytes[10] === 0x42 && bytes[11] === 0x50
) {
return '.webp';View on GitHub (pinned to 5be4028344)
Solutions
- Inspect the full Gemini response — look at promptFeedback.blockReason and candidates[].finishReason first.
- If blocked, reword the prompt to avoid flagged categories and retry.
- Confirm ctx.wireModel resolves to the image-capable Gemini slug (gemini-2.5-flash-image family), not the text variant.
- If finishReason is MAX_TOKENS, ensure the request body does not cap outputTokens below image-payload size.
Example fix
// before
throw new Error('nano-banana image response missing candidates[].content.parts[].inlineData.data');
// after — surface why nothing came back
const block = data?.promptFeedback?.blockReason;
const reasons = (data?.candidates || []).map(c => c?.finishReason).join(',');
throw new Error(
`nano-banana image produced no inlineData (block=${block || 'none'}, finishReasons=[${reasons || 'none'}])`,
); Defensive patterns
Strategy: type-guard
Validate before calling
// Inspect block/finish reasons before declaring 'missing inlineData'
function extractGeminiImageBytes(data: any): Buffer {
const candidates = Array.isArray(data?.candidates) ? data.candidates : [];
for (const c of candidates) {
for (const p of c?.content?.parts || []) {
if (typeof p?.inlineData?.data === 'string' && p.inlineData.data) {
return Buffer.from(p.inlineData.data, 'base64');
}
}
}
const block = data?.promptFeedback?.blockReason;
const finish = candidates.map((c: any) => c?.finishReason).join(',') || 'none';
throw new Error(`nano-banana produced no image (block=${block || 'none'}, finish=[${finish}])`);
} Type guard
interface GeminiImagePart { inlineData?: { data?: string } }
function hasGeminiInlineImage(data: any): boolean {
return Array.isArray(data?.candidates) && data.candidates.some((c: any) =>
Array.isArray(c?.content?.parts) && c.content.parts.some((p: GeminiImagePart) =>
typeof p?.inlineData?.data === 'string' && p.inlineData.data.length > 0));
} Try / catch
try {
bytes = extractGeminiImageBytes(data);
} catch (e) {
if (/block=/.test(String(e))) { /* content filter — surface to user as 'prompt blocked' */ }
throw e;
} Prevention
- Check promptFeedback.blockReason and candidate finishReason before declaring missing image.
- Confirm the model slug is the image-capable Gemini variant.
- Ensure request maxOutputTokens is high enough to fit the image payload.
- Reword prompts that trip safety filters instead of retrying unchanged.
When it happens
Trigger: Gemini returned `candidates:[]` (nothing generated), `promptFeedback.blockReason` set (safety block with no candidate), candidates whose parts contain only text (a textual refusal), or an empty `inlineData.data` field.
Common situations: Prompt tripped Gemini safety filters; the model variant selected is the text-only flash and not the image flash; the key lacks the image generation scope; the response was truncated by maxOutputTokens before the image landed.
Related errors
- nano-banana image non-JSON: ${truncate(text, 200)}
- grok image response missing b64_json/url
- no Nano Banana API key — configure it in Settings or set OD_
- nano-banana image ${resp.status}: ${truncate(text, 240)}
- openrouter image response contained no images for model ${wi
AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12).
Data as JSON: /api/errors/5b8a0f3c89db5368.
Report an issue: GitHub.