GPTMap

OpenAI Structured Outputs: The Complete Guide to json_schema, strict Mode, and Common Errors

The full path to reliably parseable JSON: the Responses API text.format syntax, strict mode, schema design tips, and troubleshooting for high-frequency errors like passing schema without name.

TL;DR
Structured outputs make the model return parseable JSON conforming to your JSON Schema. Responses API syntax: text={"format": {"type": "json_schema", "name": ..., "strict": true, "schema": ...}} -- schema inside format, name required (schema alone errors out), strict forces exact conformance. Includes Python / Node examples and a troubleshooting table.
Structured outputs are OpenAI API's constrained-decoding capability: you provide a JSON Schema in the request, and the model is constrained to emit JSON conforming to that schema -- no regex extraction or format repair, straight into json.loads / JSON.parse in your application code.

How to

  1. Define the JSON Schema

    Write the desired output shape as a JSON Schema: top-level type: object, fields under properties, required fields in required, and enum for closed sets. The more fields, the more the descriptions matter.

  2. Wrap it in text.format with a name

    Add text={"format": {"type": "json_schema", "name": a-config-name, "strict": true, "schema": your-schema}} to the request. name is required -- passing only the schema errors out.

  3. Send the request with input

    The Responses API message field is input (an array), not messages. Put system instructions and user input both in input.

  4. Parse output_text defensively

    Take resp.output_text and json.loads it; the shape is guaranteed by the schema, but keep one business-semantic check (enum validity, date ranges) in application code.

Structured outputs are OpenAI API's constrained-decoding capability: you provide a JSON Schema in the request, and the model is constrained to emit JSON conforming to that schema -- no regex extraction or format repair, straight into json.loads / JSON.parse in your application code. This tutorial covers the complete Responses API syntax, what strict mode actually guarantees, and troubleshooting for the most common errors (starting with passing the schema without a name).

Note: the code in this article was verified line-by-line against the official Structured Outputs guide (docs-checked version); the lastTestedAt field marks the verification date. Run it in a test environment before production use.

1. Quick Start: A Minimal Working Example

In the Responses API, structured outputs hang off the text parameter. Inside format you have the trio type, name, and schema, plus the strict switch.

from openai import OpenAI

client = OpenAI()

schema = {
    "type": "object",
    "properties": {
        "steps": {
            "type": "array",
            "items": {"type": "string"},
            "description": "Solution steps in order",
        },
        "answer": {"type": "string", "description": "The final answer"},
    },
    "required": ["steps", "answer"],
}

resp = client.responses.create(
    model="gpt-5.6-luna",
    input=[
        {"role": "system", "content": "You are a math tutor. Follow the schema exactly."},
        {"role": "user", "content": "A word problem: Amy has 3 apples and buys 5 more. How many?"},
    ],
    text={
        "format": {
            "type": "json_schema",
            "name": "math_response",
            "strict": True,
            "schema": schema,
        }
    },
)

data = json.loads(resp.output_text)
print(data["answer"], data["steps"])

Three places where mistakes are most likely: the message field is input, not messages; the schema is wrapped inside text.format, not at the top level; and name is required. The official guide's example is exactly this shape: text={"format": {"type": "json_schema", "name": "math_response", "schema": ...}}.

2. The Node.js Equivalent

import OpenAI from "openai";

const client = new OpenAI();

const schema = {
  type: "object",
  properties: {
    sentiment: { type: "string", enum: ["positive", "neutral", "negative"] },
    score: { type: "number" },
  },
  required: ["sentiment", "score"],
};

const resp = await client.responses.create({
  model: "gpt-5.6-luna",
  input: [
    { role: "system", content: "Classify user reviews. Follow the schema exactly." },
    { role: "user", content: "Battery life is great, but the fit is mediocre." },
  ],
  text: {
    format: {
      type: "json_schema",
      name: "sentiment_response",
      strict: true,
      schema,
    },
  },
});

const data = JSON.parse(resp.output_text);
console.log(data.sentiment, data.score);

Enums are the most useful constraint in structured outputs: for classification tasks, write the label space into the schema and the model cannot output values outside it.

3. strict Mode: Guaranteed Shape, Not Semantics

strict: true means constrained decoding -- the output is generated to follow the schema exactly, with field presence and types guaranteed. Omitted or false, the model "tries" to comply and can drift out of valid JSON on long text or deep nesting.

Separate the two layers of guarantee:

  • Shape guarantee (what strict gives): fields exist, types match, enum values stay in the set.
  • Semantic correctness (what strict does not give): whether answer is the right answer, whether sentiment matches human judgment -- that depends on your prompts and the model.

So the production setup is double insurance: strict ensures json.loads never throws, and the application keeps one semantic validation pass (enum validity, numeric ranges, date formats) with retry or fallback on failure.

data = json.loads(resp.output_text)

# Shape is guaranteed by strict; this checks the semantics strict does not
assert data["sentiment"] in {"positive", "neutral", "negative"}
if not 0 <= data["score"] <= 1:
    raise ValueError("score out of range -- retry or fall back")

4. Schema Design Tips

  • Top level is always an object: hang all fields off the top-level properties instead of a bare array -- parsing and validation stay simple.
  • Fill in required: put every field you expect into required; under strict mode, explicit required beats hoping fields show up.
  • Prefer enums over free text: any closed set (positive/neutral/negative, low/mid/high) should be an enum, constraining the model to your label space.
  • Descriptions are for the model: each field's description is where the model reads semantic constraints -- put format requirements (like "YYYY-MM-DD") right there.
  • Do complex validation in the app: regexes and cross-field consistency checks do not belong in the schema; validate after the response in application code.

5. Common Errors and Troubleshooting

SymptomCauseFix
Error when passing only the schemaformat is missing nameAdd the name field -- it is required
Unknown parameter messagesUsed the Chat Completions fieldThe Responses API uses input (an array)
Output is occasionally invalid JSONstrict: true not setTurn strict on; do not rely on "best effort"
Enum values outside the setNo enum in the schema, or strict offAdd enum to the field and enable strict
Valid JSON but wrong valuesShape is fine, semantics driftedStrengthen descriptions and the system prompt; add business validation
Long nested output gets cut offOutput token limit reachedSimplify the schema, split the task, or raise the output limit

The "schema without name" failure is the most common -- the error message does not always point at name, so people waste time hunting through schema syntax. Remember the trio: type, name, schema, plus the strict switch.

6. Structured Outputs or Function Calling?

They share the JSON Schema description language but solve different problems:

DimensionFunction callingStructured outputs
What it solvesThe model decides which tool to call and generates argumentsConstrains the JSON shape of the final answer
Who initiatesThe model emits a function_call; your code executesYou initiate the request and get the result back
Typical useQuery a database, call internal APIs, perform actionsClassification, extraction, tagging, fixed-format content
Flowfunction_call -> execute -> function_call_outputThe response directly contains schema-conformant text

One line: use function calling when the model should "do things," structured outputs when it should "submit homework in your format." Both ride on the Responses API's tool and format mechanisms with a consistent declaration style -- learn one and the other is nearly free.

Frequently Asked Questions

1. How do structured outputs relate to function calling?

They share the same schema machinery but solve different problems: function calling lets the model decide which tool to call and generate arguments; structured outputs constrain the JSON shape of the final answer. Use structured outputs when downstream code consumes the answer; use function calling when the model should trigger capabilities on your side. A single request can use just one of them.

2. Why does it error when I only pass the schema?

In the Responses API the schema must be wrapped in text.format, and format must include a name field -- name identifies this output configuration. Passing only {"type": "json_schema", "schema": ...} without name errors out. See the first example for the complete shape.

3. What is the difference between strict: true and omitting strict?

strict: true is constrained decoding: the output follows the schema exactly, with field presence and types guaranteed. Without it the model "tries" to comply and can drift on long or deeply nested content. Turn strict on everywhere the output goes straight into json.loads.

4. Can I use regexes or custom formats in the schema?

The well-supported subset is JSON Schema's core: object, array, string, number, enum, and required. Exotic features have weaker compatibility -- express constraints with basic types plus enum, and do complex validation in your application.

5. The JSON is valid but the field values are wrong -- what now?

Structured outputs guarantee shape, not semantics. Put semantic constraints into field descriptions and the system prompt (annotate enum values, give date format examples), and keep a business-level validation pass in the application -- valid shape plus semantic checks is the production-grade setup.

6. Does the Chat Completions response_format still work?

Legacy projects can keep using response_format, but Chat Completions is in legacy status; new projects should standardize on the Responses API (the input field plus text.format). All examples in this article use the Responses API.

Next Steps

Key points

  • In the Responses API pass text={"format": {"type": "json_schema", "name": ..., "strict": true, "schema": ...}} -- the schema is wrapped in format and name is required
  • Passing only the schema without name errors out immediately -- the single most common failure
  • strict: true means the output follows the schema exactly; omitting it (or false) means the model only 'tries' to comply
  • The messages field is input (an array), not messages -- new projects standardize on the Responses API
  • Function calling and structured outputs are different things: the former decides which tool to call, the latter constrains the shape of the answer
  • Parse defensively anyway: the schema constrains shape, not business semantics

Frequently asked questions

They share the same schema machinery but solve different problems: function calling lets the model decide which tool to call and generate arguments; structured outputs constrain the JSON shape of the final answer. Use structured outputs when downstream code consumes the answer; use function calling when the model should trigger capabilities on your side. A single request can use just one of them.

Official references

Related articles

Subscribe to GPTMap Weekly

One email every Monday: curated OpenAI updates, deep dives, and best practices. No ads, unsubscribe anytime.

GPTMap EditorialPublished 2026-08-30 8 min read
Test environment (EEAT)
Last tested: 2026-08-30
Model used: gpt-5.6