{"record":{"id":"6f414623cc889135","repo":"moeru-ai/airi","slug":"nano-banana-api-error","errorCode":null,"errorMessage":"Nano Banana API Error","messagePattern":"Nano Banana API Error","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/stage-tamagotchi/src/main/services/airi/widgets/providers/nanobanana.ts","lineNumber":83,"sourceCode":"    try {\n      const url = `https://generativelanguage.googleapis.com/v1beta/models/${model}:generateContent?key=${this.apiKey}`\n      const generationParts: any[] = [{ text: prompt }]\n      if (base64Image) {\n        generationParts.push({ inline_data: { mime_type: 'image/jpeg', data: base64Image } })\n      }\n\n      const response = await fetch(url, {\n        method: 'POST',\n        headers: { 'Content-Type': 'application/json' },\n        body: JSON.stringify({\n          contents: [{ parts: generationParts }],\n          generationConfig: { imageConfig: { aspectRatio: '1:1', imageSize: resolution } },\n        }),\n      })\n\n      const json = await response.json()\n      if (json.error) {\n        throw new Error(json.error.message || 'Nano Banana API Error')\n      }\n\n      // Search all parts for the first image\n      const responseParts = json.candidates?.[0]?.content?.parts || []\n      const imagePart = responseParts.find((p: any) => p.inlineData?.data)\n      const inlineData = imagePart?.inlineData\n\n      if (inlineData?.data) {\n        const dataUrl = `data:${inlineData.mimeType};base64,${inlineData.data}`\n        this.updateStatus(jobId, { status: 'succeeded', progress: 100, imageUrl: dataUrl })\n      }\n      else {\n        throw new Error('No image data returned from Nano Banana')\n      }\n    }\n    catch (e: any) {\n      log.error(`[Nano Banana] Generation failed: ${e.message}`)\n      this.updateStatus(jobId, { status: 'failed', error: e.message })","sourceCodeStart":65,"sourceCodeEnd":101,"githubUrl":"https://github.com/moeru-ai/airi/blob/27111382b4a79a7e983289d6e983a06af185ed0f/apps/stage-tamagotchi/src/main/services/airi/widgets/providers/nanobanana.ts#L65-L101","documentation":"Thrown by the Nano Banana provider's generation flow when the API response JSON contains an error object. The error.message is used if present, otherwise the generic 'Nano Banana API Error'. The request POSTs a Gemini-style contents payload with imageConfig and reads the response JSON synchronously.","triggerScenarios":"The Nano Banana (Gemini image) API returned a 4xx/5xx with a JSON body containing { error: { message } } — invalid API key, quota exceeded, model not available for the key, malformed request, safety/content filter, or billing disabled.","commonSituations":"API key is invalid or revoked; free-tier quota exhausted; the requested model isn't enabled for the project; the image part format is wrong (base64 cleansing left invalid data); safety filter triggered by the prompt; billing not set up on the Google AI project.","solutions":["Read json.error.message to get the provider's specific failure reason and address it.","Verify the API key is valid and has the Nano Banana (Gemini image) model enabled.","Check quota/billing on the Google AI project and upgrade or wait for quota reset.","Sanitize the prompt and image input to avoid content-filter rejections."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Validate key/quota assumptions before calling (best-effort)\nif (!this.apiKey) throw new Error('Nano Banana API Key not configured')\n// cannot fully pre-validate server-side errors; rely on response handling","typeGuard":"function isNanoBananaError(json: any): boolean {\n  return Boolean(json && typeof json === 'object' && json.error)\n}","tryCatchPattern":"try {\n  return await provider.generate(request)\n} catch (e) {\n  const msg = errorMessageFrom(e) ?? ''\n  if (/API Key|API Error|quota/i.test(msg)) {\n    return { error: `Nano Banana request rejected: ${msg}` }\n  }\n  throw e\n}","preventionTips":["Keep the API key valid and the project billing/quota healthy.","Confirm the requested model is enabled for the key.","Sanitize prompt/image inputs to avoid content-filter rejections."],"tags":["nanobanana","artistry","api","providers","credentials"],"backgroundTag":null,"analyzedSha":"27111382b4a79a7e983289d6e983a06af185ed0f","analyzedAt":"2026-08-12T18:33:34.132Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}