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

  1. 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.
  2. Verify the key is valid at aistudio.google.com by issuing a trivial generateContent request.
  3. Confirm credentials.model or ctx.wireModel is a current Gemini image model slug.
  4. 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

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


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