Understanding Gemini API JSON and Structured Output Issues
Gemini API's JSON Mode and Structured Output are powerful features that let you receive AI responses in machine-parseable JSON format. However, when building production applications, you'll inevitably run into issues like unparseable JSON, schema violations, or truncated responses that break your data pipeline.
This guide covers the most common JSON output error patterns, explains why they happen, and walks you through concrete fixes with Python and JavaScript code examples. Whether you're dealing with malformed responses or mysterious empty outputs, you'll find a solution here.
The 5 Most Common JSON Output Error Patterns
Before diving into solutions, identify which problem you're facing. Gemini API structured output issues fall into five distinct categories.
Pattern 1: Response Isn't JSON at All
You've set response_mime_type to application/json, but the model returns plain text or Markdown instead. This is especially common when migrating from Gemini 1.5 to Gemini 2.5 series models.
Pattern 2: JSON Syntax Errors (Parse Failures)
The response looks like JSON but throws an exception when you call JSON.parse() or json.loads(). Common culprits include extra code block markers (```), trailing commas, or unescaped special characters.
Pattern 3: Schema Violations
The response is valid JSON, but it doesn't match your response_schema. Fields are missing, types are wrong (string instead of number), or enum values aren't respected.
Pattern 4: Truncated JSON (Incomplete Output)
When requesting large outputs, the JSON object ends abruptly without closing brackets. The finish_reason shows MAX_TOKENS, meaning the model ran out of output space before completing the structure.
Pattern 5: Empty or Null Responses
Despite having a valid schema, the API returns an empty string or null. This is typically caused by safety filters blocking the request before any JSON generation begins.
Root Cause Analysis: Why JSON Output Fails
Understanding the underlying causes helps you apply the right fix immediately.
Missing or Incorrect response_mime_type
The most fundamental cause is not setting response_mime_type correctly in your request configuration. SDK parameter names can vary between versions, so always check the latest documentation.
# ❌ Wrong: not including response_mime_type in generation config
model = genai.GenerativeModel("gemini-2.5-flash")
response = model.generate_content("Return user data as JSON")
# ✅ Correct: explicitly setting response_mime_type
model = genai.GenerativeModel(
"gemini-2.5-flash",
generation_config=genai.GenerationConfig(
response_mime_type="application/json"
)
)
response = model.generate_content("Return user data as JSON")
print(response.text)
# Expected: {"name": "John Doe", "age": 30, "email": "john@example.com"}Incomplete Schema Definitions
When your response_schema is underspecified, the model fills in gaps with its own interpretation. This is the primary cause of schema violations.
# ❌ Insufficient schema (no type specifications)
schema = {"type": "object", "properties": {"score": {}}}
# ✅ Complete schema (types, required fields, and descriptions)
schema = {
"type": "object",
"properties": {
"score": {
"type": "number",
"description": "Evaluation score from 0 to 100"
},
"category": {
"type": "string",
"enum": ["positive", "negative", "neutral"],
"description": "Sentiment category"
}
},
"required": ["score", "category"]
}Insufficient max_output_tokens
The default token limit often isn't enough for complex JSON structures. This is the most common cause of truncated output, especially when generating arrays with many items.
Safety Filter Blocks
If the input content triggers safety policies, the model blocks the request before generating any JSON output, resulting in empty or null responses.
Step-by-Step Solutions
Step 1: Verify Your SDK and Model Version
Start by confirming you're using a current SDK version and a model that supports JSON Mode.
# Check and update Python SDK
pip install --upgrade google-genai
python -c "import google.genai; print(google.genai.__version__)"// Verify Node.js SDK loads correctly
const { GoogleGenAI } = require("@google/genai");
console.log("SDK loaded successfully");
// Update to latest: npm install @google/genai@latestModels with full JSON Mode and Structured Output support:
- Gemini 2.5 Pro — Full support (recommended)
- Gemini 2.5 Flash — Full support (recommended for cost efficiency)
- Gemini 2.0 Flash — Supported
- Gemini 1.5 Pro / Flash — Basic support (some limitations)
Step 2: Configure response_mime_type and response_schema Correctly
This is the single most important step for reliable JSON output.
import google.genai as genai
client = genai.Client(api_key="YOUR_API_KEY")
# Define a complete schema
product_schema = {
"type": "object",
"properties": {
"products": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {"type": "string", "description": "Product name"},
"price": {"type": "integer", "description": "Price in USD"},
"in_stock": {"type": "boolean", "description": "Availability"}
},
"required": ["name", "price", "in_stock"]
}
}
},
"required": ["products"]
}
response = client.models.generate_content(
model="gemini-2.5-flash",
contents="Generate info for 3 products: laptop, mouse, keyboard",
config=genai.types.GenerateContentConfig(
response_mime_type="application/json",
response_schema=product_schema,
max_output_tokens=2048
)
)
import json
data = json.loads(response.text)
print(json.dumps(data, indent=2))
# Expected output:
# {
# "products": [
# {"name": "Laptop", "price": 899, "in_stock": true},
# {"name": "Mouse", "price": 39, "in_stock": true},
# {"name": "Keyboard", "price": 79, "in_stock": false}
# ]
# }const { GoogleGenAI } = require("@google/genai");
const ai = new GoogleGenAI({ apiKey: "YOUR_API_KEY" });
const productSchema = {
type: "object",
properties: {
products: {
type: "array",
items: {
type: "object",
properties: {
name: { type: "string", description: "Product name" },
price: { type: "integer", description: "Price in USD" },
in_stock: { type: "boolean", description: "Availability" }
},
required: ["name", "price", "in_stock"]
}
}
},
required: ["products"]
};
async function generateProducts() {
const response = await ai.models.generateContent({
model: "gemini-2.5-flash",
contents: "Generate info for 3 products: laptop, mouse, keyboard",
config: {
responseMimeType: "application/json",
responseSchema: productSchema,
maxOutputTokens: 2048
}
});
const data = JSON.parse(response.text);
console.log(JSON.stringify(data, null, 2));
}
generateProducts();Step 3: Increase max_output_tokens for Large Responses
If your JSON output is getting truncated, bump up the token limit.
config = genai.types.GenerateContentConfig(
response_mime_type="application/json",
response_schema=product_schema,
max_output_tokens=8192 # Set higher than default
)As a rough guide, each JSON character consumes about 1–2 tokens. For JSON responses with 1,000+ fields, set max_output_tokens to at least 8192.
Step 4: Implement Response Validation
Never trust model output blindly. Always validate the parsed JSON on your end.
import json
import re
def validate_and_parse(response_text, expected_keys=None):
"""Safe JSON parser for Gemini API responses"""
text = response_text.strip()
# Remove code block markers
if text.startswith("```"):
text = text.split("\n", 1)[1] if "\n" in text else text[3:]
if text.endswith("```"):
text = text[:-3]
text = text.strip()
try:
data = json.loads(text)
except json.JSONDecodeError as e:
print(f"JSON parse error: {e}")
# Attempt to fix trailing commas
text = re.sub(r',\s*([}\]])', r'\1', text)
try:
data = json.loads(text)
print("Recovered after removing trailing commas")
except json.JSONDecodeError:
raise ValueError(f"Unrecoverable JSON: {text[:200]}...")
# Verify expected keys exist
if expected_keys:
missing = [k for k in expected_keys if k not in data]
if missing:
raise ValueError(f"Missing keys: {missing}")
return data
# Usage
result = validate_and_parse(response.text, expected_keys=["products"])
print(f"Products received: {len(result['products'])}")Step 5: Check finish_reason and Implement Retry Logic
Use finish_reason to detect incomplete responses and retry automatically.
def generate_with_retry(client, prompt, schema, max_retries=3):
"""Auto-retry based on finish_reason"""
for attempt in range(max_retries):
response = client.models.generate_content(
model="gemini-2.5-flash",
contents=prompt,
config=genai.types.GenerateContentConfig(
response_mime_type="application/json",
response_schema=schema,
max_output_tokens=8192
)
)
candidate = response.candidates[0]
finish_reason = candidate.finish_reason
if finish_reason == "STOP":
try:
return json.loads(response.text)
except json.JSONDecodeError:
print(f"Attempt {attempt + 1}: JSON parse failed, retrying...")
continue
elif finish_reason == "MAX_TOKENS":
print(f"Attempt {attempt + 1}: Output truncated, retrying...")
continue
elif finish_reason == "SAFETY":
print(f"Attempt {attempt + 1}: Blocked by safety filter")
raise ValueError("Safety filter blocked the response")
else:
print(f"Attempt {attempt + 1}: Unexpected: {finish_reason}")
continue
raise RuntimeError(f"Failed after {max_retries} retries")Verification: Confirming the Fix Works
After applying the fixes, run these verification steps to confirm everything is working.
Test 1: Basic JSON output
test_response = client.models.generate_content(
model="gemini-2.5-flash",
contents="Generate a sample person with name and age",
config=genai.types.GenerateContentConfig(
response_mime_type="application/json",
response_schema={
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"}
},
"required": ["name", "age"]
}
)
)
data = json.loads(test_response.text)
assert isinstance(data["name"], str), "name should be string"
assert isinstance(data["age"], int), "age should be integer"
print("✅ Basic JSON test passed")Test 2: Large array output
Verify that arrays with many items don't get truncated.
large_response = client.models.generate_content(
model="gemini-2.5-flash",
contents="List all 50 US states with their capitals",
config=genai.types.GenerateContentConfig(
response_mime_type="application/json",
response_schema={
"type": "object",
"properties": {
"states": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {"type": "string"},
"capital": {"type": "string"}
},
"required": ["name", "capital"]
}
}
},
"required": ["states"]
},
max_output_tokens=8192
)
)
data = json.loads(large_response.text)
print(f"✅ Large data test: {len(data['states'])} states returned")Test 3: Confirm finish_reason
candidate = large_response.candidates[0]
assert candidate.finish_reason == "STOP", f"Unexpected: {candidate.finish_reason}"
print("✅ finish_reason is STOP (complete response)")Prevention: Best Practices to Avoid Recurrence
Always Define Complete Schemas
Specify type, description, and required for every field. Ambiguity leads to unpredictable model behavior and inconsistent outputs.
Build Validation into Production Code
AI model outputs are inherently non-deterministic. Always implement try/catch blocks with type checking, and have fallback handling for parse failures.
Chunk Large Outputs
Instead of requesting 50 items in one call, split your requests into batches of 10–15 items each. This dramatically reduces the risk of truncation.
Reinforce with System Instructions
In addition to response_mime_type and response_schema, add System Instructions stating "Return only valid JSON. Do not wrap the response in code blocks." This further reduces the rate of malformed output.
Keep Your SDK Updated
The Google AI Python SDK receives frequent updates with bug fixes for structured output. Run pip install --upgrade google-genai regularly to stay current.
Looking back
Most Gemini API JSON output issues come down to three things: correctly configuring response_mime_type and response_schema, setting sufficient max_output_tokens, and implementing proper response validation. The five error patterns and solutions covered in this guide should resolve the vast majority of structured output problems you'll encounter in production.
For broader error handling strategies, see our Gemini API Error Handling Complete Guide. If you're new to JSON Mode, start with our JSON Mode and Structured Output guide.
To build a solid foundation in API design and error handling,