nexu-io/open-design · error · Error
nano-banana image ${resp.status}: ${truncate(text, 240)}
Error message
nano-banana image ${resp.status}: ${truncate(text, 240)} What it means
Thrown by renderNanoBananaImage when the POST to `${baseUrl}/v1beta/models/<model>:generateContent` returns a non-2xx status. The message embeds the HTTP status and the first 240 chars of the body so the operator can see xAI/Google's error detail inline. This is the generic 'the API rejected the request' path.
Source
Thrown at apps/daemon/src/media/index.ts:1708
imageSize: NANOBANANA_DEFAULT_IMAGE_SIZE,
},
},
};
const resp = await fetchImageGenerationWithResponseRetry(
() => fetch(`${baseUrl}/v1beta/models/${encodeURIComponent(wireModel)}:generateContent`, withMediaRequestInit(ctx, {
method: 'POST',
headers: nanoBananaHeaders(baseUrl, apiKey),
body: JSON.stringify(body),
})),
(summary) => ctx.onProviderRequestSettled?.({
providerId: 'nanobanana',
...summary,
}),
);
const text = await resp.text();
if (!resp.ok) {
throw new Error(`nano-banana image ${resp.status}: ${truncate(text, 240)}`);
}
let data: any;
try {
data = JSON.parse(text);
} catch {
throw new Error(`nano-banana image non-JSON: ${truncate(text, 200)}`);
}
const bytes = inlineImageBytesFromGenerateContent(data);
return {
bytes,
providerNote: `nano-banana/${wireModel} · ${nanoBananaAspectFor(ctx.aspect)} · ${NANOBANANA_DEFAULT_IMAGE_SIZE} · ${bytes.length} bytes`,
suggestedExt: sniffImageExt(bytes),
};
}
function nanoBananaHeaders(baseUrl: string, apiKey: string): Record<string, string> {
const headers: Record<string, string> = {
'content-type': 'application/json',View on GitHub (pinned to 5be4028344)
Solutions
- Read the embedded status+body in the error: 401/403 → rotate/regenerate the key; 429 → wait and/or upgrade quota; 404 → fix the model id.
- Verify the key is valid at aistudio.google.com by issuing a trivial generateContent request.
- Confirm credentials.model or ctx.wireModel is a current Gemini image model slug.
- If 5xx, retry with backoff or check the Google AI status dashboard.
Example fix
// before
if (!resp.ok) {
throw new Error(`nano-banana image ${resp.status}: ${truncate(text, 240)}`);
}
// after — classify common failures for clearer operator action
if (!resp.ok) {
const hint = resp.status === 401 || resp.status === 403 ? ' (check API key)'
: resp.status === 429 ? ' (quota/rate limit)'
: resp.status === 404 ? ' (unknown model slug)'
: '';
throw new Error(`nano-banana image ${resp.status}${hint}: ${truncate(text, 240)}`);
} Defensive patterns
Strategy: try-catch
Validate before calling
// Map common HTTP statuses to actionable hints before throwing
function describeNanoBananaHttpError(status: number, body: string): string {
const hint = status === 401 || status === 403 ? 'check API key'
: status === 429 ? 'quota/rate limit reached'
: status === 404 ? 'unknown model slug'
: status >= 500 ? 'upstream Google AI outage — retry'
: 'see body for detail';
return `nano-banana image ${status} (${hint}): ${truncate(body, 240)}`;
} Type guard
function isRetryableNanoBananaStatus(status: number): boolean {
return status === 429 || status >= 500;
} Try / catch
let resp: Response;
try {
resp = await nanoBananaFetch(ctx, credentials);
} catch (e) {
// network error — single retry with backoff
await sleep(2000);
resp = await nanoBananaFetch(ctx, credentials);
}
if (!resp.ok) {
if (isRetryableNanoBananaStatus(resp.status)) { /* one retry */ }
throw new Error(describeNanoBananaHttpError(resp.status, await resp.text()));
} Prevention
- Classify HTTP statuses into retryable (429, 5xx) vs terminal (401/403/404) and retry accordingly.
- Embed status-specific hints in the error so the operator can act without reading the body.
- Verify the key works at aistudio.google.com before relying on it in production.
- Keep the model slug in credentials.model validated against Google's published list.
When it happens
Trigger: Invalid/expired API key (401/403), quota exhaustion (429), malformed model id (404), unsupported aspect/size for the model (400), or upstream 5xx. The truncate(text,240) helper caps long HTML error pages so the log stays readable.
Common situations: Free-tier AI Studio key hitting rate limits, model name typo (e.g. missing 'flash-image' suffix), key revoked, or Google API outage.
Related errors
- no Nano Banana API key — configure it in Settings or set OD_
- nano-banana image non-JSON: ${truncate(text, 200)}
- nano-banana image response missing candidates[].content.part
- openrouter image ${resp.status}: ${truncate(text, 240)}
- no OpenRouter API key — configure it in Settings or set OPEN
AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12).
Data as JSON: /api/errors/0b5c72ab65bc2234.
Report an issue: GitHub.