Fixing Google Gemini API JSON Output Errors: A Practical Guide
Gemini API returning malformed or truncated JSON? This guide covers every error pattern with fixes and defensive coding strategies.
Have broken JSON right now? Fix it free in under 1 second — no signup.
Fix My JSON →Google's Gemini API is increasingly used in production pipelines that require structured output. While Gemini has strong JSON capabilities, especially with the response_mime_type and response_schema features, the output isn't always clean. This guide covers every failure pattern you're likely to encounter and how to handle them in both Node.js and Python.
Gemini JSON Output Quirks
Before diving into fixes, it helps to understand what makes Gemini's JSON output distinctive compared to other models:
- Markdown wrapping by default — Without explicit configuration, Gemini wraps JSON in
`json`code fences - Truncation on large schemas — When
max_output_tokensis hit, the JSON is cut off mid-structure - Extra whitespace and newlines — Gemini tends to add more whitespace than necessary, which is harmless but requires stripping before passing to size-sensitive systems
- Schema non-compliance — Even with
response_schemaset, Gemini occasionally adds extra fields or changes field names to title case - Safety filter interruption — A response can be cut short by Gemini's safety filters, leaving incomplete JSON
Method 1: response_mime_type: 'application/json'
The single most effective change you can make. Setting the MIME type tells Gemini you want raw JSON output, not Markdown-formatted text.
Node.js
import { GoogleGenerativeAI } from '@google/generative-ai'
const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY)
const model = genAI.getGenerativeModel({
model: 'gemini-1.5-pro',
generationConfig: {
responseMimeType: 'application/json',
temperature: 0.1 // Lower temperature = more deterministic structure
}
})
const result = await model.generateContent(
'List the top 3 programming languages in 2026 with their primary use case. Return as JSON array.'
)
const text = result.response.text()
try {
const data = JSON.parse(text)
console.log(data)
} catch (err) {
console.error('Still got invalid JSON despite mime type:', text.slice(0, 500))
// Fall back to extraction + repair
}
Python
import google.generativeai as genai
import json
import os
genai.configure(api_key=os.environ["GEMINI_API_KEY"])
model = genai.GenerativeModel(
model_name="gemini-1.5-pro",
generation_config=genai.GenerationConfig(
response_mime_type="application/json",
temperature=0.1
)
)
response = model.generate_content(
"Extract the product name, price, and availability from: "
"'The AirPods Pro 3 costs $279 and is in stock.'"
)
try:
data = json.loads(response.text)
print(data)
except json.JSONDecodeError as e:
print(f"JSON parse error at pos {e.pos}: {e.msg}")
print(f"Raw response: {response.text[:500]}")
Note: response_mime_type reduces but does not eliminate malformed output. Gemini may still add a BOM, a leading newline, or occasionally a fence block in edge cases. Always wrap parsing in a try/except.
Method 2: response_schema for Typed Output
For Gemini 1.5 Pro and later, you can provide an explicit schema that the model will follow:
Python with Schema
import google.generativeai as genai
from google.generativeai import types
import json
genai.configure(api_key=os.environ["GEMINI_API_KEY"])
Define your schema using Gemini's Schema type
product_schema = types.Schema(
type=types.Type.OBJECT,
properties={
"name": types.Schema(type=types.Type.STRING),
"price_usd": types.Schema(type=types.Type.NUMBER),
"in_stock": types.Schema(type=types.Type.BOOLEAN),
"tags": types.Schema(
type=types.Type.ARRAY,
items=types.Schema(type=types.Type.STRING)
)
},
required=["name", "price_usd", "in_stock"]
)
model = genai.GenerativeModel(
model_name="gemini-1.5-pro",
generation_config=genai.GenerationConfig(
response_mime_type="application/json",
response_schema=product_schema,
temperature=0
)
)
response = model.generate_content(
"Extract product data: 'The ProDesk Monitor 4K costs $599.99, is available, and is tagged: "
"monitor, 4K, USB-C'"
)
data = json.loads(response.text)
Node.js with Schema
import { GoogleGenerativeAI, SchemaType } from '@google/generative-ai'
const model = genAI.getGenerativeModel({
model: 'gemini-1.5-pro',
generationConfig: {
responseMimeType: 'application/json',
responseSchema: {
type: SchemaType.OBJECT,
properties: {
name: { type: SchemaType.STRING },
price_usd: { type: SchemaType.NUMBER },
in_stock: { type: SchemaType.BOOLEAN }
},
required: ['name', 'price_usd', 'in_stock']
},
temperature: 0
}
})
Common Error Patterns from Gemini
Pattern 1: Markdown-Wrapped JSON (Without response_mime_type)
What you receive:
Here's the JSON data you requested:json
{
"name": "Alice",
"age": 30
}
Fix:
function extractGeminiJSON(text) {
// Strip markdown fences
const match = text.match(/
(?:json)?\s\n?([\s\S]?)\n?```/)
if (match) return match[1].trim()
// Strip leading prose before first { or [
const jsonStart = Math.min(
text.indexOf('{') === -1 ? Infinity : text.indexOf('{'),
text.indexOf('[') === -1 ? Infinity : text.indexOf('[')
)
if (jsonStart < Infinity) return text.slice(jsonStart).trim()
return text.trim()
}
Pattern 2: Truncated JSON
When max_output_tokens is too low, the response is cut off:
json
{
"users": [
{"id": 1, "name": "Alice"},
{"id": 2, "name": "Bob"},
{"id": 3, "name": "Char
Detection and handling:python
response = model.generate_content(prompt)
Check finish reason
candidate = response.candidates[0]
if candidate.finish_reason.name == "MAX_TOKENS":
print("Warning: response was truncated")
# Option 1: retry with higher max_output_tokens
# Option 2: paste raw text into aijsonmedic.com for truncation repair
# Option 3: redesign prompt to request smaller batches
Check for safety filter interruption
if candidate.finish_reason.name == "SAFETY":
print("Response interrupted by safety filter")
For truncated JSON, the JSON Fixer can close unclosed strings, arrays, and objects — recovering partial output rather than discarding it entirely.
Pattern 3: Extra Fields / Field Name Drift
Gemini sometimes adds fields not in your schema or changes product_name to productName:
json
{
"productName": "Widget", // Should be product_name
"Price": 29.99, // Should be price_usd
"additional_notes": "..." // Not in schema at all
}
Defensive extraction:python
def extract_fields(data: dict, schema_fields: list[str]) -> dict:
"""Extract only the expected fields, case-insensitively."""
result = {}
data_lower = {k.lower().replace("-", "_"): v for k, v in data.items()}
for field in schema_fields:
key_lower = field.lower().replace("-", "_")
if key_lower in data_lower:
result[field] = data_lower[key_lower]
return result
raw = json.loads(response.text)
clean = extract_fields(raw, ["product_name", "price_usd", "in_stock"])
Pattern 4: Array Wrapped in Object
Gemini sometimes wraps a requested array in an object:
json
{"items": [1, 2, 3]}
// Instead of: [1, 2, 3]
python
data = json.loads(response.text)
If you expected an array but got an object, check for wrapper
if isinstance(data, dict) and len(data) == 1:
key = next(iter(data))
if isinstance(data[key], list):
data = data[key] # unwrap
Retry Strategy
python
import time
import json
def fetch_json_with_retry(model, prompt: str, max_attempts: int = 3) -> dict:
last_error = None
for attempt in range(1, max_attempts + 1):
try:
response = model.generate_content(prompt)
if not response.candidates:
raise ValueError("No candidates in response — possible safety block")
candidate = response.candidates[0]
if candidate.finish_reason.name == "MAX_TOKENS":
raise ValueError("Response truncated — increase max_output_tokens")
raw_text = response.text
data = json.loads(raw_text.strip())
return data
except json.JSONDecodeError as e:
last_error = e
print(f"Attempt {attempt}: JSON parse error at pos {e.pos}: {e.msg}")
if attempt < max_attempts:
# Add explicit correction to prompt for next attempt
prompt = (
f"{prompt}\n\n"
"IMPORTANT: Your previous response was not valid JSON. "
"Return ONLY a raw JSON object — no markdown, no code fences, "
"no explanatory text."
)
time.sleep(0.5 * attempt)
except Exception as e:
last_error = e
print(f"Attempt {attempt}: {e}")
if attempt < max_attempts:
time.sleep(0.5 * attempt)
raise RuntimeError(f"Failed after {max_attempts} attempts: {last_error}")
Full Production Example: Node.js
javascript
import { GoogleGenerativeAI, SchemaType } from '@google/generative-ai'
const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY)
function extractJSON(text) {
const match = text.match(/``(?:json)?\s\n?([\s\S]?)\n?``/)
return match ? match[1].trim() : text.trim()
}
async function geminiJSON(prompt, schema = null, maxAttempts = 3) {
const config = {
responseMimeType: 'application/json',
temperature: 0.1
}
if (schema) config.responseSchema = schema
const model = genAI.getGenerativeModel({
model: 'gemini-1.5-pro',
generationConfig: config
})
for (let i = 0; i < maxAttempts; i++) {
const result = await model.generateContent(prompt)
const candidate = result.response.candidates?.[0]
if (!candidate) throw new Error('No response candidate')
if (candidate.finishReason === 'MAX_TOKENS') {
throw new Error('Response truncated — increase maxOutputTokens')
}
const raw = result.response.text()
try {
return JSON.parse(extractJSON(raw))
} catch (err) {
if (i === maxAttempts - 1) {
// Log raw output for manual review at aijsonmedic.com
console.error('Gemini JSON repair needed. Raw output:', raw)
throw new Error(Invalid JSON after ${maxAttempts} attempts: ${err.message})
}
// Retry with stronger instruction
prompt = ${prompt}\n\nReturn ONLY raw JSON. No markdown fences.
}
}
}
// Usage
const data = await geminiJSON(
'List 3 countries with their capital cities and populations as a JSON array.',
{
type: SchemaType.ARRAY,
items: {
type: SchemaType.OBJECT,
properties: {
country: { type: SchemaType.STRING },
capital: { type: SchemaType.STRING },
population: { type: SchemaType.INTEGER }
}
}
}
)
```
Diagnosing Broken Gemini Output
When a Gemini response fails to parse, the fastest path to diagnosis is:
- Log the full raw
response.text()to your console - Paste it into the JSON Fixer — it identifies every issue and shows a repaired version
- Use the JSON Validator to verify the repaired output matches your expected schema
- Use the JSON Formatter to pretty-print the output before debugging large responses
- Use the JSON Diff Tool to compare the broken output against the valid version you expected
Summary
| Problem | Solution |
|---|---|
| Markdown fences | Set responseMimeType: 'application/json' |
| Schema non-compliance | Use response_schema with typed schema |
| Truncation | Increase max_output_tokens or batch into smaller prompts |
| Extra/renamed fields | Defensive field extraction by normalized key |
| Parse failure | Retry with corrective prompt, then manual repair via JSON Fixer |
| Safety interruption | Check finish_reason, redesign prompt to avoid trigger |
The combination of responseMimeType + response_schema + low temperature covers the vast majority of Gemini JSON reliability issues. For the edge cases that slip through, automated retry and the JSON Fixer handle the rest.
FAQ
How do I reliably get JSON from the Gemini API?
Set response_mime_type: "application/json" in the generation config and define a response_schema using Gemini's schema format. Together, these constrain the model to produce valid JSON matching your schema. Lower temperature to 0.2 or below for consistent structured output. Still implement a retry with correction on parse failure as a safety net.
Why does Gemini sometimes return JSON with extra commentary?
Without response_mime_type: "application/json", Gemini follows its default conversational behavior and may add preamble ("Here is the JSON you requested:") or postscript. Set the MIME type constraint, or use a stricter system instruction: "Return only a JSON object matching the following schema. No explanation, no markdown, no commentary."
What is the Gemini equivalent of OpenAI's function calling for structured output?
Use Gemini's function declarations with function_calling_config: { mode: "ANY" }. Define the output shape as a function parameter schema, and Gemini fills in the structured response. This is more reliable than free-form JSON generation for complex schemas. In the Gemini API, this is accessed through the tools parameter with function_declarations.
How do I handle Gemini safety filter interruptions that break JSON?
When a safety filter triggers mid-response, the output is truncated. Check candidates[0].finish_reason — a value of SAFETY means safety filtering stopped the response. You cannot recover the JSON from a safety-interrupted response. Redesign your prompt to avoid triggering filters: remove explicit references to sensitive topics and use neutral framing.
Why does Gemini Flash produce more JSON errors than Gemini Pro?
Flash is optimized for speed and cost, which means it's more likely to deviate from structured output constraints under complex prompts. For JSON-critical production use cases, use Gemini 1.5 Pro or Gemini 2.0 Pro, which follow schema constraints more reliably. Flash is appropriate for high-volume classification or extraction tasks where occasional parse failures can be handled with retry logic.
Still dealing with broken JSON?
Paste it in and get it fixed in under 1 second — free, no signup, no install. Works with ChatGPT, Claude, n8n, and any AI output.
Fix My JSON Free →Related Articles