Structured OutputsでJSON Schemaエラーになる原因|required・additionalPropertiesの直し方

Structured OutputsでJSON Schemaエラーになる原因|required・additionalPropertiesの直し方 AI開発

OpenAI APIのStructured Outputsを使おうとしたところ、Invalid schemaやJSON Schema関連のエラーが出て、リクエスト自体が失敗することがあります。

通常のJSON Schemaとしては問題なさそうに見えても、Structured Outputsではそのまま利用できないSchemaがあります。

特に間違いやすいのが、requiredadditionalPropertiesです。

OpenAIのStructured Outputsでは、基本的にpropertiesで定義したすべてのフィールドをrequiredへ含める必要があり、objectにはadditionalProperties: falseを指定する必要があります。また、通常のJSON Schemaで利用できる機能すべてに対応しているわけではありません。OpenAI公式ドキュメントでも、Structured Outputsが対応するのはJSON Schemaのサブセットであると説明されています。

この記事では、Structured OutputsでJSON Schemaエラーになる代表的な原因と、requiredadditionalPropertiesを正しく設定する方法を解説します。ZodやPydanticを使った実装の全体像、拒否・未完了の処理、業務ルールの検証などはLLMのJSON出力が壊れる原因|Structured Outputs・Zodで安全に検証する方法で解説しているため、この記事ではJSON Schemaレベルでの原因切り分けに絞ります。

スポンサーリンク

Structured Outputsとは

Structured Outputsは、AIから返されるJSONを指定したJSON Schemaに従わせるための機能です。

単に「JSONで返してください」とプロンプトへ書く場合と違い、Structured Outputsでは、フィールド名やデータ型、enumなどをSchemaとして定義できます。

OpenAIによると、Structured Outputsではモデルのレスポンスを指定したJSON Schemaへ準拠させることができ、必須キーの欠落や存在しないenum値などを防ぎやすくなります。

たとえば次のようなレスポンスを必ず返したいとします。

期待するレスポンス
{
  "title": "Structured Outputsとは",
  "category": "AI",
  "published": true
}

この場合、Schemaを次のように定義できます。

Schema定義
{
  "type": "object",
  "properties": {
    "title": {
      "type": "string"
    },
    "category": {
      "type": "string"
    },
    "published": {
      "type": "boolean"
    }
  },
  "required": [
    "title",
    "category",
    "published"
  ],
  "additionalProperties": false
}

一見すると普通のJSON Schemaですが、Structured Outputsでは特に最後のrequiredadditionalPropertiesが重要になります。

最も多い原因はrequiredの指定漏れ

Structured OutputsでSchemaエラーが発生した場合、最初に確認したいのがrequiredです。

一般的なJSON Schemaでは、propertiesへフィールドを定義しても、必ずしもすべてをrequiredへ入れる必要はありません。

たとえば通常のJSON Schemaなら、次のようにtitleだけを必須にする設計もできます。

通常のJSON Schema
{
  "type": "object",
  "properties": {
    "title": {
      "type": "string"
    },
    "description": {
      "type": "string"
    }
  },
  "required": [
    "title"
  ]
}

しかしOpenAIのStructured Outputsでは、この考え方が異なります。

公式ドキュメントでは、Structured Outputsを利用する場合、すべてのフィールドまたはFunction parameterをrequiredとして指定する必要があるとされています。

そのため、propertiestitledescriptionがあるなら、次のように両方を含めます。

修正例
{
  "type": "object",
  "properties": {
    "title": {
      "type": "string"
    },
    "description": {
      "type": "string"
    }
  },
  "required": [
    "title",
    "description"
  ],
  "additionalProperties": false
}

propertiesには存在するのにrequiredへ入っていないフィールドがある場合、Structured OutputsではSchemaが拒否される可能性があります。

任意項目はrequiredから外すのではなくnullを使う

ここで疑問になるのが、「任意項目を作りたい場合はどうするのか」という点です。

たとえば記事データを生成するとして、descriptionが存在しないケースを許可したいとします。

通常のJSON Schemaならdescriptionrequiredから外したくなります。

しかしStructured Outputsでは、フィールドそのものはrequiredにしたまま、型としてnullを許可します。

nullを許容するSchema
{
  "type": "object",
  "properties": {
    "title": {
      "type": "string"
    },
    "description": {
      "type": [
        "string",
        "null"
      ]
    }
  },
  "required": [
    "title",
    "description"
  ],
  "additionalProperties": false
}

このSchemaなら、値が存在するときは次のようになります。

値がある場合
{
  "title": "Structured Outputsとは",
  "description": "JSON Schemaに従った出力を生成する機能です"
}

値がない場合は次のようにできます。

値がない場合
{
  "title": "Structured Outputsとは",
  "description": null
}

OpenAI公式ドキュメントでも、すべてのフィールドをrequiredにしたうえで、任意項目を表現したい場合はnullとのunionを使う方法が案内されています。

「optionalだからrequiredから消す」のではなく、「キーは必ず存在するが、値としてnullを許可する」と考えると分かりやすいでしょう。

JavaScript/TypeScriptでZodから生成する場合も同じ制約を受けます。optional()ではなくnullable()を使う具体的な書き方はLLMのJSON出力が壊れる原因|Structured Outputs・Zodで安全に検証する方法の「OptionalではなくNullableを使う」で解説しています。

additionalProperties: falseがないとエラーになる

requiredと並んで非常に多い原因が、additionalPropertiesの指定漏れです。

次のSchemaを見てみます。

不完全なSchema
{
  "type": "object",
  "properties": {
    "title": {
      "type": "string"
    }
  },
  "required": [
    "title"
  ]
}

通常のJSON Schemaとして見ると、それほど不自然ではありません。

しかしStructured Outputsでは、objectに対してadditionalProperties: falseを指定する必要があります。

修正すると次のようになります。

修正例
{
  "type": "object",
  "properties": {
    "title": {
      "type": "string"
    }
  },
  "required": [
    "title"
  ],
  "additionalProperties": false
}

additionalPropertiesは、propertiesへ定義していない追加フィールドを許可するかどうかを決める設定です。

たとえば次のSchemaを考えます。

additionalProperties: falseの例
{
  "type": "object",
  "properties": {
    "title": {
      "type": "string"
    }
  },
  "required": [
    "title"
  ],
  "additionalProperties": false
}

この場合、想定されている出力は次のようなものです。

想定されている出力
{
  "title": "AI開発入門"
}

一方、モデルが勝手に次のようなフィールドを追加することは許可されません。

許容されない出力
{
  "title": "AI開発入門",
  "author": "AI",
  "score": 95
}

OpenAIはStructured Outputsについて、Schemaで指定したキーと値だけを生成するため、objectではadditionalProperties: falseを必須としていると説明しています。

ネストしたobjectにもadditionalProperties: falseが必要

特に見落としやすいのが、Schemaをネストした場合です。

ルートのobjectだけにadditionalProperties: falseを書けばよいと思ってしまうことがあります。

たとえば次のSchemaです。

不完全なネストSchema
{
  "type": "object",
  "properties": {
    "article": {
      "type": "object",
      "properties": {
        "title": {
          "type": "string"
        },
        "category": {
          "type": "string"
        }
      },
      "required": [
        "title",
        "category"
      ]
    }
  },
  "required": [
    "article"
  ],
  "additionalProperties": false
}

外側にはadditionalProperties: falseがありますが、articleのobjectにはありません。

Structured Outputsで利用するなら、内側のobjectにも追加します。

修正例
{
  "type": "object",
  "properties": {
    "article": {
      "type": "object",
      "properties": {
        "title": {
          "type": "string"
        },
        "category": {
          "type": "string"
        }
      },
      "required": [
        "title",
        "category"
      ],
      "additionalProperties": false
    }
  },
  "required": [
    "article"
  ],
  "additionalProperties": false
}

ネストしたSchemaでエラーになる場合は、ルートだけを見るのではなく、すべてのtype: "object"を確認するのが重要です。

OpenAI公式のネストされたSchema例でも、それぞれのobjectへadditionalProperties: falseが設定されています。

array内のobjectでも同じルールが適用される

配列の中にobjectを入れる場合も同様です。

たとえば記事一覧を返すSchemaを考えます。

不完全なSchema
{
  "type": "object",
  "properties": {
    "articles": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "title": {
            "type": "string"
          },
          "url": {
            "type": "string"
          }
        },
        "required": [
          "title",
          "url"
        ]
      }
    }
  },
  "required": [
    "articles"
  ],
  "additionalProperties": false
}

この場合もitems内のobjectにadditionalProperties: falseがありません。

次のように修正します。

修正例
{
  "type": "object",
  "properties": {
    "articles": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "title": {
            "type": "string"
          },
          "url": {
            "type": "string"
          }
        },
        "required": [
          "title",
          "url"
        ],
        "additionalProperties": false
      }
    }
  },
  "required": [
    "articles"
  ],
  "additionalProperties": false
}

Schemaが大きくなるほど、配列内部のobjectへ設定するのを忘れやすくなります。

additionalPropertiesエラーが消えない場合は、Schema全体から"type": "object"を検索すると見つけやすいでしょう。

requiredにはpropertiesの全キーを入れる

requiredを書いているのにエラーになる場合は、配列の中身も確認します。

次のSchemaではrequired自体は存在しています。

不完全なSchema
{
  "type": "object",
  "properties": {
    "name": {
      "type": "string"
    },
    "age": {
      "type": "integer"
    },
    "country": {
      "type": "string"
    }
  },
  "required": [
    "name",
    "age"
  ],
  "additionalProperties": false
}

しかしpropertiesにはcountryもあります。

Structured Outputsではすべてのフィールドをrequiredとして扱う必要があるため、次のように修正します。

修正例
{
  "type": "object",
  "properties": {
    "name": {
      "type": "string"
    },
    "age": {
      "type": "integer"
    },
    "country": {
      "type": "string"
    }
  },
  "required": [
    "name",
    "age",
    "country"
  ],
  "additionalProperties": false
}

Schemaを修正するときは、requiredの有無だけでなく、

確認ポイント1
propertiesに存在するキー

確認ポイント2
requiredに存在するキー

が完全に一致しているか確認してください。

ルートをarrayにするとエラーになる

requiredadditionalPropertiesを修正してもエラーが残る場合は、Schemaのルートを確認します。

Structured Outputsでは、ルートレベルはobjectである必要があります。

たとえば記事一覧だけを返したいからといって、次のようにルートをarrayにする設計は避けます。

避ける設計
{
  "type": "array",
  "items": {
    "type": "string"
  }
}

代わりにobjectで包みます。

推奨する設計
{
  "type": "object",
  "properties": {
    "items": {
      "type": "array",
      "items": {
        "type": "string"
      }
    }
  },
  "required": [
    "items"
  ],
  "additionalProperties": false
}

OpenAI公式ドキュメントでは、Structured OutputsのルートSchemaはobjectである必要があり、ルートでanyOfを利用することもできないとされています。

ZodのdiscriminatedUnion()のように内部でanyOfを生成するヘルパーをルートへ直接使った場合も同じ理由で失敗します。この場合にルートを単一objectへまとめる具体的な設計はLLMのJSON出力が壊れる原因|Structured Outputs・Zodで安全に検証する方法の「ルートをオブジェクトにする」で解説しています。anyOf自体が完全に禁止されているわけではなく、ルート以外の位置では各Schemaが対応条件を満たせば利用できます。

JSON Schemaのすべての機能が使えるわけではない

Structured Outputsで重要なのは、JSON Schemaに対応しているからといって、JSON Schemaの全機能が利用できるわけではないという点です。

OpenAI公式ドキュメントでは、Structured OutputsはJSON Schemaのサブセットに対応すると明記されています。基本的な型としてstring、number、boolean、integer、object、array、enum、anyOfなどを利用できます。

一方、現在はallOfnotdependentRequireddependentSchemasifthenelseなどには対応していません。strict: trueで未対応のJSON Schemaを送信するとエラーになると説明されています。

外部ライブラリが自動生成した巨大なJSON SchemaをそのままStructured Outputsへ渡している場合、この制限に引っかかる可能性があります。

$refを使った再帰的なSchema(親子関係を持つツリー構造など)は技術的には利用できますが、ネストが深くなるほど不安定になりやすいと報告されています。ツリー構造のデータを扱う場合は、Schemaを再帰させるより、親IDを持たせたフラットなリストへ変換して表現するほうが安全です。

fine-tunedモデルではSchema制約がさらに異なる

ファインチューニングしたモデルを利用している場合は、通常モデルとの差にも注意が必要です。

OpenAI公式ドキュメントでは、fine-tuned modelsについて、stringのminLengthmaxLengthpatternformat、numberのminimummaximum、arrayのminItemsmaxItemsなど、一部の制約がさらにサポートされないとされています。

そのため、

制約の例
{
  "type": "string",
  "maxLength": 100
}

のようなSchemaが、利用するモデルによって問題になるケースもあります。

Schemaエラーがモデル変更後に発生した場合は、モデル自体がStructured Outputsへ対応しているかだけでなく、そのモデルで利用できるJSON Schema制約も確認してください。

Structured Outputs非対応モデルを使っていないか確認する

Schemaが正しくても、利用しているモデル自体がStructured Outputsへ対応していなければ利用できません。

2026年9月時点では、たとえばGPT-5.6 SolはStructured Outputsに対応しています。OpenAIのモデルページでもFunction CallingとStructured Outputsの両方がSupportedとされています。

一方、すべてのOpenAIモデルがStructured Outputsへ対応しているわけではありません。

音声専用モデルや一部の特殊モデルなどでは利用できないことがあります。

モデルを変更した直後からStructured Outputsが失敗するようになった場合は、モデルページのStructured outputs欄を確認するとよいでしょう。

Responses APIならtext.formatを確認する

OpenAI APIでは、利用しているAPIによってStructured Outputsの指定場所が異なります。

現在のResponses APIでは、text.formatとしてSchemaを指定できます。

JavaScript SDKとZodを利用する場合、たとえば次のように書けます。

src/extract-article.ts
import OpenAI from "openai";
import { zodTextFormat } from "openai/helpers/zod";
import { z } from "zod";

const openai = new OpenAI();

const Article = z.object({
  title: z.string(),
  category: z.string(),
  description: z.string().nullable()
});

const response = await openai.responses.parse({
  model: "gpt-5.6",
  input: "AI開発についての記事情報を作成してください",
  text: {
    format: zodTextFormat(Article, "article")
  }
});

console.log(response.output_parsed);

現在のOpenAI公式ドキュメントでも、Responses APIではresponses.parse()text.formatを利用する例が案内されています。

古い記事やChat Completions用のサンプルと混ぜると、JSON Schemaそのものではなくリクエスト形式の問題でエラーになることがあります。

Chat Completionsではresponse_formatを使う

Chat Completions APIを使う場合は形式が異なります。

Zodを利用する公式例では次のようにresponse_formatを指定します。

src/extract-article-chat.ts
const completion = await openai.chat.completions.parse({
  model: "gpt-5.6",
  messages: [
    {
      role: "user",
      content: "記事データを生成してください"
    }
  ],
  response_format: zodResponseFormat(Article, "article")
});

Responses APIではtext.format、Chat Completionsではresponse_formatという違いがあります。

JSON Schemaエラーだと思ってSchemaだけを何度も変更しても直らない場合は、使用しているエンドポイントとサンプルコードが一致しているか確認してください。

手書きSchemaでは最小構成から試す

巨大なSchemaでエラーが発生した場合、最初からすべてを確認するのは大変です。

その場合は一度Schemaを最小構成まで減らすと原因を切り分けやすくなります。

まず次の程度まで単純化します。

最小構成
{
  "type": "object",
  "properties": {
    "result": {
      "type": "string"
    }
  },
  "required": [
    "result"
  ],
  "additionalProperties": false
}

これで成功するなら、モデルやAPI呼び出し自体ではなく、元のSchemaに原因がある可能性が高くなります。

その後、objectやarrayを1つずつ戻していけば、どの部分でSchemaエラーが発生するのか特定しやすくなります。

Schemaが大きすぎる場合にも制限がある

Structured OutputsにはSchemaサイズにも上限があります。

OpenAI公式ドキュメントによると、Schema全体で利用できるobject propertyは最大100個、ネストは最大5階層です。また、property名・definition名・enum値・const値を合計した文字列長は15,000文字以下、enum値の総数は500個までという上限もあります。

通常の小規模なレスポンスSchemaで問題になることは少ないですが、データベースSchemaやOpenAPI Schemaなどから巨大なJSON Schemaを自動生成している場合には注意が必要です。

「Schemaの内容は正しいはずなのに、大きくした途端にエラーになった」という場合はサイズ制限も確認してください。これらの上限値はOpenAIのアップデートによって変更される可能性があるため、最新の公式ドキュメントもあわせて確認することをおすすめします。

additionalPropertiesをtrueにして柔軟なJSONを作ることはできない

Structured Outputsを使いながら、「基本的なフィールドだけ決めて、それ以外はAIに自由に追加させたい」という設計を考える場合があります。

たとえば次のようなSchemaです。

使えない設計
{
  "type": "object",
  "properties": {
    "title": {
      "type": "string"
    }
  },
  "required": [
    "title"
  ],
  "additionalProperties": true
}

Structured Outputsではこのような用途には向きません。

OpenAIは、Structured Outputsでは指定されたキーと値だけを生成するため、additionalProperties: falseを要求しています。

自由な追加データが必要なら、

metadataで自由度を確保
{
  "type": "object",
  "properties": {
    "title": {
      "type": "string"
    },
    "metadata": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "key": {
            "type": "string"
          },
          "value": {
            "type": "string"
          }
        },
        "required": [
          "key",
          "value"
        ],
        "additionalProperties": false
      }
    }
  },
  "required": [
    "title",
    "metadata"
  ],
  "additionalProperties": false
}

のように、自由度が必要な部分そのものをSchemaとして表現する方法を検討できます。

「JSONとして正しい」と「Structured Outputsで正しい」は別

Structured Outputsのエラーで混乱しやすい最大の理由は、JSON、JSON Schema、Structured Outputsの制約を混同してしまうことです。

次のSchemaはJSONとしては正しい場合があります。

JSONとしては正しい
{
  "type": "object",
  "properties": {
    "name": {
      "type": "string"
    }
  }
}

JSON Schemaとしても一般的には成立します。

しかしStructured Outputsでは、

Structured Outputsでの正解
{
  "type": "object",
  "properties": {
    "name": {
      "type": "string"
    }
  },
  "required": [
    "name"
  ],
  "additionalProperties": false
}

のように、さらにStructured Outputs側の条件を満たす必要があります。

そのため、オンラインのJSON Schema Validatorで「Valid」と表示されたとしても、OpenAI APIでもそのまま利用できるとは限りません。

requiredエラーが出たときの確認方法

required関連でエラーになった場合は、Schema内の各objectを順番に確認します。

たとえば次の部分があれば、

propertiesの例
"properties": {
  "name": {},
  "email": {},
  "age": {}
}

対応するrequiredは、

requiredの例
"required": [
  "name",
  "email",
  "age"
]

となっている必要があります。

ネストしたobjectがある場合は、その階層でも同じ確認を行います。

外側のrequiredが正しくても、内側で1項目だけ抜けていればSchema全体が失敗する可能性があります。

additionalPropertiesエラーが出たときの確認方法

additionalProperties関連の場合は、Schema全体から次の記述を探します。

検索対象
"type": "object"

見つかったobjectそれぞれに、

必要な設定
"additionalProperties": false

が存在するか確認します。

特に見落としやすいのは、arrayのitemsanyOf内部、$defs内、複数階層にネストされたobjectです。

OpenAI公式の$defsを利用した例でも、定義されたobjectそれぞれにrequiredadditionalProperties: falseが設定されています。

Structured OutputsのJSON Schemaエラーに関するよくある質問

Qrequiredにすべてのフィールドを入れると任意項目は作れませんか

A作れます。任意項目はrequiredから外すのではなく、フィールドをrequiredへ残したまま型をstringとnullのunion(例: [“string”, “null”])にします。値が存在しない場合はキー自体を省略するのではなく、値としてnullを返す設計にします。

QadditionalPropertiesはルートオブジェクトだけに書けばよいですか

Aいいえ。ネストしたobject、arrayのitems内のobject、$defs内のobjectなど、Schema内に存在するすべてのtype: objectへadditionalProperties: falseが必要です。ルートだけに設定してエラーが消えない場合は、Schema全体からtype: objectを検索して確認してください。

QStructured OutputsとZodのoptional/nullableの違いは何ですか

AStructured Outputsではすべてのフィールドがrequired扱いになるため、Zodのoptional()は制約と合わずエラーの原因になります。任意項目にはnullable()を使用します。具体的な書き方は関連記事のLLMのJSON出力が壊れる原因で解説しています。

QSchemaのサイズ制限はどのくらいですか

AOpenAI公式ドキュメントによると、Schema全体で利用できるobject propertyは最大100個、ネストは最大5階層です。property名・definition名・enum値・const値を合計した文字列長は15,000文字以下という上限もあります。外部ツールで自動生成した巨大なSchemaではこの制限に注意してください。

Qルートをarrayにできないのはなぜですか

AStructured OutputsのルートSchemaはobjectである必要があり、ルートでarrayやanyOfを使うことはできません。配列を返したい場合も、items配列をプロパティとして持つobjectで包む設計にします。

Qfine-tunedモデルでは何が変わりますか

A通常モデルより制約が増えます。stringのminLength・maxLength・pattern・format、numberのminimum・maximum、arrayのminItems・maxItemsなど、一部の制約がさらにサポートされない場合があります。モデル変更後にSchemaエラーが出た場合は、そのモデルで利用できる制約を確認してください。

QSchemaエラーの原因を効率よく調べる方法はありますか

ASchemaを最小構成(1つのstringプロパティだけなど)まで減らしてから、objectやarrayを1つずつ戻していく方法が有効です。どの段階でエラーが再発するかを確認すれば、原因となっている箇所を特定しやすくなります。

まとめ

Structured OutputsでInvalid schemaなどのエラーが発生した場合、複雑なプロンプトやモデルの挙動を調べる前にSchemaを確認するのが近道です。

特に重要なのは、propertiesで定義したフィールドがすべてrequiredへ入っていることと、すべてのobjectへadditionalProperties: falseが設定されていることです。

任意項目が必要だからといってrequiredから削除するのではなく、string | nullなどnullableな型として定義します。

それでも直らなければ、ルートがobjectになっているか、ルートにanyOfを置いていないか、Structured Outputsで未対応のJSON Schema機能を使用していないか、Schemaサイズの上限を超えていないか、モデルがStructured Outputsへ対応しているかを確認します。

Structured OutputsはJSONの形式崩れを防ぐ強力な機能ですが、通常のJSON Schemaより制約が強いため、「一般的なJSON Schemaとして正しいか」ではなく、「OpenAIのStructured Outputsが対応しているJSON Schemaか」という視点で確認することが重要です。