GEMINI LABEN
NANOLITE — Nano Banana 2 Liteが登場しました。Googleで最も速く、最もコスト効率の高いGemini Imageモデルで、軽量な画像生成を安く回したい用途に向いていますOMNIFLASH — Gemini Omni Flashがpublic previewになりました。ネイティブにマルチモーダルなモデルで、企業や開発者が独自の動的な動画ワークフローを構築できますAGENTS — Managed Agentsが拡張されました。background: trueでサーバー側の非同期実行とポーリング、リモートMCPサーバー連携、対話をまたぐ認証情報のリフレッシュに対応しますMEMORY — Memory BankのIngestEvents APIが一般提供になりました。イベントの取り込みとメモリ生成を分離し、コンテンツを継続的にストリームできますTHROUGHPUT — Provisioned Throughputで、同一モデル・同一リージョンに対して最大7件の保留オーダーを提出できるようになりましたDEPRECATE — 画像生成モデルは8月17日に、Gemini Enterprise Agent PlatformのGrok 4.1系は8月20日に停止される予定ですNANOLITE — Nano Banana 2 Liteが登場しました。Googleで最も速く、最もコスト効率の高いGemini Imageモデルで、軽量な画像生成を安く回したい用途に向いていますOMNIFLASH — Gemini Omni Flashがpublic previewになりました。ネイティブにマルチモーダルなモデルで、企業や開発者が独自の動的な動画ワークフローを構築できますAGENTS — Managed Agentsが拡張されました。background: trueでサーバー側の非同期実行とポーリング、リモートMCPサーバー連携、対話をまたぐ認証情報のリフレッシュに対応しますMEMORY — Memory BankのIngestEvents APIが一般提供になりました。イベントの取り込みとメモリ生成を分離し、コンテンツを継続的にストリームできますTHROUGHPUT — Provisioned Throughputで、同一モデル・同一リージョンに対して最大7件の保留オーダーを提出できるようになりましたDEPRECATE — 画像生成モデルは8月17日に、Gemini Enterprise Agent PlatformのGrok 4.1系は8月20日に停止される予定です
記事一覧/API / SDK
API / SDK/2026-04-10中級

Gemini APIのJSON出力・構造化出力が正しく返らない原因と対処法

Gemini APIのJSON Modeや構造化出力で不正なJSON・スキーマ違反・途中切れが発生する原因を分析し、具体的な解決手順をコード例付きで解説します。

gemini-api279structured-output20json-mode2troubleshooting57error14fix10

Gemini APIのJSON出力・構造化出力で起きる問題とは

Gemini APIのJSON Modeや**Structured Output(構造化出力)**は、AIの応答を機械的に処理しやすいJSON形式で受け取るための強力な機能です。しかし、実際に本番アプリケーションで利用していると「JSONがパースできない」「スキーマ通りの値が返らない」「レスポンスが途中で切れている」といった問題に直面することがあります。

コード例はすべてPythonとJavaScript(Node.js)の両方で示しているため、お使いの環境に合わせてすぐに適用できます。

症状別:よくあるJSON出力エラー5パターン

Gemini APIの構造化出力に関するトラブルは、大きく5つのパターンに分類できます。まずは自分が遭遇している問題がどれに該当するかを確認しましょう。

パターン1:レスポンスがJSON形式ではない

response_mime_typeapplication/json に設定しているにもかかわらず、プレーンテキストやMarkdown形式で応答が返ってくるケースです。特にGemini 1.5系からGemini 2.5系へ移行した直後に発生しやすい問題です。

パターン2:JSONの構文エラー(パースできない)

レスポンスは一見JSON形式に見えるものの、JSON.parse()json.loads() で例外が発生するパターンです。余分なコードブロック記号(```)が付与されていたり、末尾にカンマが残っていたりすることが原因です。

パターン3:スキーマ定義と異なる構造が返る

response_schema で指定したフィールドが欠損している、型が異なる(数値を期待しているのに文字列で返る)、enum値が守られないなどの問題です。

パターン4:レスポンスが途中で切れている(Truncated JSON)

長い出力を要求した際に、JSONオブジェクトが閉じきらないまま finish_reasonMAX_TOKENS になるパターンです。配列の要素を大量に生成する場合に特に起きやすい問題です。

パターン5:空のレスポンスまたはnullが返る

スキーマを指定しているにもかかわらず、空文字列や null が返ってくるパターンです。セーフティフィルターによるブロックが原因であることが多いです。

原因の分析:なぜJSON出力が失敗するのか

各パターンの根本原因を理解することが、確実な解決への第一歩です。

response_mime_type の設定ミス

最も基本的な原因は、リクエスト時に response_mime_type が正しく設定されていないことです。SDKのバージョンによってパラメータ名が異なる場合もあるため注意が必要です。

# ❌ 間違い:generation_config に含めていない
model = genai.GenerativeModel("gemini-2.5-flash")
response = model.generate_content("ユーザーデータをJSON形式で返してください")
 
# ✅ 正しい:generation_config で明示的に指定
model = genai.GenerativeModel(
    "gemini-2.5-flash",
    generation_config=genai.GenerationConfig(
        response_mime_type="application/json"
    )
)
response = model.generate_content("ユーザーデータをJSON形式で返してください")
print(response.text)
# 期待出力: {"name": "田中太郎", "age": 30, "email": "tanaka@example.com"}

スキーマ定義の不備

response_schema に渡すスキーマが不完全だと、モデルは自己判断で構造を補完しようとします。これがスキーマ違反の主な原因です。

# ❌ 不十分なスキーマ(型指定なし)
schema = {"type": "object", "properties": {"score": {}}}
 
# ✅ 完全なスキーマ(型・必須フィールド・説明を明記)
schema = {
    "type": "object",
    "properties": {
        "score": {
            "type": "number",
            "description": "0から100までの評価スコア"
        },
        "category": {
            "type": "string",
            "enum": ["positive", "negative", "neutral"],
            "description": "感情カテゴリ"
        }
    },
    "required": ["score", "category"]
}

max_output_tokens の不足

デフォルトのトークン数制限では、複雑なJSON構造を完全に出力しきれない場合があります。特に配列を含む出力で頻発します。

セーフティフィルターによるブロック

入力内容がセーフティポリシーに抵触すると、モデルはJSON出力を生成する前にリクエストをブロックし、空のレスポンスや null を返します。

解決手順:Step by Stepガイド

Step 1:SDKとモデルバージョンを確認する

まず、使用しているSDKが最新であること、そしてJSON Modeをサポートしているモデルを使用していることを確認します。

# Python SDK のバージョン確認と更新
pip install --upgrade google-genai
python -c "import google.genai; print(google.genai.__version__)"
// Node.js SDK のバージョン確認
const { GoogleGenAI } = require("@google/genai");
console.log("SDK loaded successfully");
 
// package.json で最新バージョンを確認
// npm install @google/genai@latest

JSON Mode / Structured Output をサポートしているモデルは以下の通りです。

  • Gemini 2.5 Pro — 完全対応(推奨)
  • Gemini 2.5 Flash — 完全対応(コスト重視の場合に推奨)
  • Gemini 2.0 Flash — 対応
  • Gemini 1.5 Pro / Flash — 基本対応(一部制約あり)

Step 2:response_mime_typeresponse_schema を正しく設定する

JSON出力を確実に得るための最も重要な設定です。

import google.genai as genai
 
client = genai.Client(api_key="YOUR_API_KEY")
 
# スキーマ定義
product_schema = {
    "type": "object",
    "properties": {
        "products": {
            "type": "array",
            "items": {
                "type": "object",
                "properties": {
                    "name": {"type": "string", "description": "商品名"},
                    "price": {"type": "integer", "description": "価格(円)"},
                    "in_stock": {"type": "boolean", "description": "在庫あり"}
                },
                "required": ["name", "price", "in_stock"]
            }
        }
    },
    "required": ["products"]
}
 
response = client.models.generate_content(
    model="gemini-2.5-flash",
    contents="以下の3商品の情報を生成してください:ノートPC、マウス、キーボード",
    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, ensure_ascii=False, indent=2))
# 期待出力:
# {
#   "products": [
#     {"name": "ノートPC", "price": 89800, "in_stock": true},
#     {"name": "マウス", "price": 3980, "in_stock": true},
#     {"name": "キーボード", "price": 7980, "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 JPY" },
          in_stock: { type: "boolean", description: "In stock" }
        },
        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:max_output_tokens を十分に確保する

JSON出力が途中で切れる場合は、トークン上限を引き上げます。

config = genai.types.GenerateContentConfig(
    response_mime_type="application/json",
    response_schema=product_schema,
    max_output_tokens=8192  # デフォルトより大きく設定
)

出力トークン数の目安として、JSON1文字あたり約1〜2トークンを消費します。1,000フィールド規模のJSONを期待する場合は max_output_tokens8192 以上に設定してください。

Step 4:レスポンスのバリデーションを実装する

モデルの出力を盲目的に信頼せず、受け取り側で必ずバリデーションを行います。

import json
 
def validate_and_parse(response_text, expected_keys=None):
    """Gemini APIレスポンスのJSON安全パーサー"""
    # コードブロック記号の除去
    text = response_text.strip()
    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}")
        # 末尾カンマの修復を試みる
        import re
        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]}...")
 
    # 期待するキーの存在チェック
    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
 
# 使用例
result = validate_and_parse(response.text, expected_keys=["products"])
print(f"取得した商品数: {len(result['products'])}")

Step 5:finish_reason を確認してリトライする

レスポンスが不完全な場合、finish_reason で原因を特定し、適切にリトライします。

def generate_with_retry(client, prompt, schema, max_retries=3):
    """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
            )
        )
 
        # finish_reason の確認
        candidate = response.candidates[0]
        finish_reason = candidate.finish_reason
 
        if finish_reason == "STOP":
            # 正常終了 — JSONパースを試行
            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, increasing tokens...")
            # トークン数を増やしてリトライ
            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: {finish_reason}")
            continue
 
    raise RuntimeError(f"Failed after {max_retries} retries")

確認方法:解決できたかの検証手順

修正を適用した後、以下の手順で問題が解決したことを確認します。

検証1:基本的なJSON出力テスト

# 最小限のテストケースで確認
test_response = client.models.generate_content(
    model="gemini-2.5-flash",
    contents="名前と年齢のサンプルデータを1件生成してください",
    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")

検証2:大量データの出力テスト

配列を含む大きなJSONが途中で切れないことを確認します。

large_response = client.models.generate_content(
    model="gemini-2.5-flash",
    contents="日本の都道府県47件すべてのデータを生成してください",
    config=genai.types.GenerateContentConfig(
        response_mime_type="application/json",
        response_schema={
            "type": "object",
            "properties": {
                "prefectures": {
                    "type": "array",
                    "items": {
                        "type": "object",
                        "properties": {
                            "name": {"type": "string"},
                            "capital": {"type": "string"}
                        },
                        "required": ["name", "capital"]
                    }
                }
            },
            "required": ["prefectures"]
        },
        max_output_tokens=8192
    )
)
data = json.loads(large_response.text)
print(f"✅ Large data test: {len(data['prefectures'])} prefectures returned")

検証3:finish_reason の確認

candidate = large_response.candidates[0]
assert candidate.finish_reason == "STOP", f"Unexpected: {candidate.finish_reason}"
print("✅ finish_reason is STOP (complete response)")

予防策:再発防止のベストプラクティス

スキーマは必ず完全に定義する

すべてのフィールドに typedescriptionrequired を明記します。曖昧さがあるとモデルの解釈に依存してしまい、出力が不安定になります。

レスポンスバリデーションを本番コードに組み込む

AIモデルの出力は原理的に非決定的です。try/except と型チェックを必ず実装し、パース失敗時のフォールバック処理を用意してください。

出力が大きくなる場合はチャンク分割する

47都道府県のように件数が多いデータを一度に要求するのではなく、「最初の10件」「次の10件」のように分割してリクエストすることで、途中切れのリスクを大幅に減らせます。

System Instructionsで出力形式を補強する

response_mime_typeresponse_schema に加えて、System Instructions で「必ず有効なJSONのみを返してください。コードブロック記号は付けないでください」と明記すると、不正な出力の発生率がさらに下がります。

SDKを定期的にアップデートする

Google AI Python SDKは頻繁にアップデートされており、構造化出力に関するバグ修正も多く含まれています。pip install --upgrade google-genai を定期的に実行しましょう。

全体を振り返って

Gemini APIのJSON出力・構造化出力のトラブルは、response_mime_typeresponse_schema の正しい設定、十分な max_output_tokens の確保、そしてレスポンスバリデーションの実装によって、ほとんどのケースを解決できます。本記事で紹介した5つのエラーパターンと対処法を参考に、安定したJSON出力を実現してください。

エラーハンドリング全般についてはGemini API エラーハンドリング完全ガイドも併せてご覧ください。また、JSON Modeの基礎から学びたい方はGemini API の JSON Mode と構造化出力ガイドが参考になります。

シェア

お読みいただきありがとうございます

Gemini Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • コピー&ペーストで使える実装コード付き
  • 毎日新しい上級ガイドを追加
  • ¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

もしこの記事がお役に立ちましたら、チップ(¥150)で応援いただけると大変励みになります。広告なしでの運営を続けるため、皆さまのご支援が大きな力になっています。

関連記事

API / SDK2026-05-31
Gemini APIで「Unsupported MIME type」エラーが出るときの原因と対処
画像や音声をGemini APIに渡したときに発生する『Unsupported MIME type』エラーを、MIMEタイプの綴り間違い・octet-stream問題・非対応フォーマットの3つの観点から切り分けて、確実に動くコードで解決します。
API / SDK2026-05-30
Gemini 2.5 Pro で thinkingBudget を 0 にすると INVALID_ARGUMENT になる原因と対処
Gemini 2.5 Pro で thinkingBudget を 0 にすると 400 INVALID_ARGUMENT が返る原因を、モデルごとの思考予算レンジの違いから解説します。Pro でレイテンシを抑える正しい書き方と Flash への切り替え判断を Python・JavaScript のコード付きで紹介します。
API / SDK2026-05-23
Gemini API の Structured Output でバリデーションエラーが返るときの原因と対処
Gemini API の responseSchema を使った構造化出力で頻発するバリデーションエラー(INVALID_ARGUMENT、schema mismatch、空配列、不要フィールド)の原因と、実運用で安定させるための実装パターンを個人開発の現場視点でまとめます。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →