{"record":{"id":"dccbf6b5d8dda9e8","repo":"n8n-io/n8n","slug":"invalid-response-format-from-gemini-api","errorCode":null,"errorMessage":"Invalid response format from Gemini API","messagePattern":"Invalid response format from Gemini API","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/@n8n/nodes-langchain/nodes/vendors/GoogleGemini/actions/image/edit.operation.ts","lineNumber":207,"sourceCode":"\t\t\t{\n\t\t\t\trole: 'user',\n\t\t\t\tparts: [...fileParts, { text: prompt }],\n\t\t\t},\n\t\t],\n\t\tgenerationConfig,\n\t};\n\n\tconst response: unknown = await apiRequest.call(\n\t\tthis,\n\t\t'POST',\n\t\t`/v1beta/${model}:generateContent`,\n\t\t{\n\t\t\tbody,\n\t\t},\n\t);\n\n\tif (!isGenerateContentResponse(response)) {\n\t\tthrow new Error('Invalid response format from Gemini API');\n\t}\n\n\tconst promises = response.candidates.map(async (candidate) => {\n\t\tconst imagePart = candidate.content.parts.find((part) => 'inlineData' in part);\n\n\t\t// Check if imagePart exists and has inlineData with actual data\n\t\tif (!imagePart?.inlineData?.data) {\n\t\t\tthrow new Error('No image data returned from Gemini API');\n\t\t}\n\n\t\tconst mimeType = imagePart.inlineData.mimeType;\n\t\tconst fileName = getFilenameFromMimeType(mimeType, 'image', 'png');\n\t\tconst bufferOut = Buffer.from(imagePart.inlineData.data, 'base64');\n\t\tconst binaryOut = await this.helpers.prepareBinaryData(bufferOut, fileName, mimeType);\n\t\treturn {\n\t\t\tbinary: {\n\t\t\t\t[outputKey]: binaryOut,\n\t\t\t},","sourceCodeStart":189,"sourceCodeEnd":225,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/@n8n/nodes-langchain/nodes/vendors/GoogleGemini/actions/image/edit.operation.ts#L189-L225","documentation":"Thrown by the GoogleGemini image edit operation when the API response from /v1beta/{model}:generateContent fails the isGenerateContentResponse type guard. This guard checks that the response is an object with a 'candidates' array where each candidate has a 'content' object with a 'parts' array. A response that doesn't match this shape is considered malformed. Note: this is a plain Error, not a NodeOperationError.","triggerScenarios":"The Gemini API returns a response without a 'candidates' array, or candidates exist but lack 'content.parts'. This can happen when the API returns an error object instead of a generation result (e.g. quota error, invalid API key, model unavailable, safety block with no candidates), when the API version changes the response schema, or when a network proxy mangles the response body.","commonSituations":"API key invalid or expired (returns error envelope, not candidates); model name incorrect or deprecated; safety filter blocks the request and returns promptFeedback without candidates; API rate limit returns error body; Gemini API version change altering response structure; proxy/gateway stripping or modifying response fields.","solutions":["Inspect the actual API response body — the error fires because it doesn't match the expected shape. Log or debug the raw response to see what the API returned.","Verify the API key is valid and has Gemini API access.","Confirm the model ID is correct and supports image generation (e.g. models/gemini-2.5-flash-image-preview).","Check for safety filter blocks — the response may contain promptFeedback with safety ratings instead of candidates. Adjust the prompt to avoid triggering filters.","Check the Google AI status page for service incidents."],"exampleFix":null,"handlingStrategy":"type-guard","validationCode":null,"typeGuard":"function isGenerateContentResponse(response: unknown): response is {\n  candidates: Array<{ content: { parts: Array<Record<string, unknown>> } }>;\n} {\n  if (typeof response !== 'object' || response === null) return false;\n  const obj = response as Record<string, unknown>;\n  if (!('candidates' in obj) || !Array.isArray(obj.candidates)) return false;\n  return obj.candidates.every((c: unknown) =>\n    typeof c === 'object' && c !== null &&\n    'content' in (c as object) &&\n    typeof (c as Record<string, unknown>).content === 'object' &&\n    'parts' in ((c as Record<string, unknown>).content as object) &&\n    Array.isArray(((c as Record<string, unknown>).content as Record<string, unknown>).parts)\n  );\n}","tryCatchPattern":"try {\n  const response = await apiRequest.call(this, 'POST', `/v1beta/${model}:generateContent`, { body });\n  if (!isGenerateContentResponse(response)) {\n    // log the raw response for debugging and surface a user-friendly error\n    console.error('Unexpected Gemini response:', JSON.stringify(response));\n    throw new Error('Gemini API returned an unexpected response format. Check API key, model ID, and safety settings.');\n  }\n} catch (error) {\n  // handle or rethrow\n  throw error;\n}","preventionTips":["Verify the Gemini API key is valid and has the correct permissions before running.","Confirm the model ID supports image generation (use 'models/gemini-2.5-flash-image-preview').","Check for safety filter blocks by inspecting the full response including promptFeedback.","Monitor Google AI Platform status for service disruptions.","Log raw API responses during development to catch schema changes early."],"tags":["api-response","type-guard","google-gemini","image-edit","validation"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}