◉GEMINI LABJP
●TTS — The three TTS preview models shut down on 12/18 per the deprecations page, not 11/17●CLI 0.68 — Gemini CLI stable is v0.68.0, with v0.69.0-preview.0 (11/10) on the preview channel●GEMINI 2.5 — gemini-2.5-pro and 2.5-flash shut down on the API on 12/1, 16 days left. Move to 3.8 Flash or 3.5 Flash-Lite●NODE 26 — On Node 26, Gemini CLI's timeout settings reportedly stop applying to model requests●OMNI 1.1 — From 12/3, requests to gemini-omni-1.1-flash are routed to 1.2 Flash automatically●NEW — Getting Consistent JSON Through the OpenAI-Compatible Layer, in Three Steps●TTS — The three TTS preview models shut down on 12/18 per the deprecations page, not 11/17●CLI 0.68 — Gemini CLI stable is v0.68.0, with v0.69.0-preview.0 (11/10) on the preview channel●GEMINI 2.5 — gemini-2.5-pro and 2.5-flash shut down on the API on 12/1, 16 days left. Move to 3.8 Flash or 3.5 Flash-Lite●NODE 26 — On Node 26, Gemini CLI's timeout settings reportedly stop applying to model requests●OMNI 1.1 — From 12/3, requests to gemini-omni-1.1-flash are routed to 1.2 Flash automatically●NEW — Getting Consistent JSON Through the OpenAI-Compatible Layer, in Three Steps
Articles/API / SDK
◈ API / SDK/2026-04-10Intermediate

How to Fix Gemini API JSON and Structured Output Errors

Troubleshoot Gemini API JSON Mode and Structured Output errors including malformed JSON, schema violations, and truncated responses with step-by-step solutions and code examples.

gemini-api291structured-output28json-modetroubleshooting98error15fix10

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@latest

Models 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,

Share

Thank You for Reading

Gemini Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • ✦Copy-paste ready implementation code
  • ✦New advanced guides published daily
  • ✦$5/mo or $10 for lifetime access
View Membership →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

◈ API / SDK2026-04-09
Classify Gemini API Errors by Status — Handling 429, SAFETY, and Token Limits in Production
Sort Gemini API failures along two axes — HTTP status and finish_reason — so you know instantly whether to wait or to fix. Covers the three distinct 429 limits, model-deprecation fallbacks, and how the exception hierarchy changes after moving to the google-genai SDK.
◈ API / SDK2026-10-19
When 404 and 429 Show Up in the Same Gemini API Log, Check the Model Name Before the Quota
A triage order for Gemini API logs that mix 404 NOT_FOUND and 429 RESOURCE_EXHAUSTED, plus a small Python script that separates a bad model name from a real quota limit without generating anything.
◈ API / SDK2026-10-16
The Day /models Returns 200 but generateContent Returns 403 — Decide Your Triage Order Before It Happens
A triage order for the case where listing models succeeds with 200 but generation fails with 403 permission_denied. Includes a small diagnostic script that checks key, project, and billing in turn, why you should not retry a 403, and how to assemble what support needs.