DevX Tool
Back to Tech Blog
AI & LLM Architecture 2026-10-17 11 min read DevX Tool Engineering Lab

Fixing OpenAI 400 "Invalid schema for response_format: 'json_schema'": Strict Schema Rules & Pydantic / Zod Fixes — DevX Tool Blog

Resolve OpenAI Structured Outputs 400 bad request errors. Fix missing additionalProperties: false, optional property definitions, and Pydantic v2 / Zod recursive schema constraints.

AI OpenAI LLM JSON Schema Python TypeScript

1. The Problem: OpenAI Structured Outputs 400 Bad Request

When migrating applications to OpenAI Structured Outputs (response_format: { type: "json_schema", ... }), developers frequently encounter a blocking 400 HTTP exception:

openai.BadRequestError: Error code: 400 - {
  "error": {
    "message": "Invalid schema for response_format 'json_schema': In context='', 'additionalProperties' is required to be supplied and to be false",
    "type": "invalid_request_error",
    "param": "response_format",
    "code": null
  }
}

OpenAI's constrained sampling engine uses a context-free grammar (CFG) compiler to guarantee 100% adherence to your JSON schema. However, for the compiler to work, your schema must adhere to a strict subset of JSON Schema Draft 7.

Interactive In-Guide ToolClient-Side Sandbox

Validate & Inspect Your JSON Schema Locally

Paste your generated JSON Schema below to verify structural formatting and ensure all required fields and properties are syntactically sound.

Input stays in your browser.
⚡ 100% Private: All calculations happen locally inside your browserDedicated Tool Page →

2. Three Mandatory Rules for OpenAI Structured Outputs

Unlike standard JSON Schema validation tools, OpenAI's schema engine enforces three non-negotiable constraints when strict: true is activated:

  1. additionalProperties: false Must Be Present on ALL Objects: Every object definition in the schema tree (including nested sub-objects) must explicitly specify "additionalProperties": false.
  2. ALL Properties Must Be Declared in required: In standard JSON Schema, omitting a key from required makes it optional. In OpenAI strict mode, all declared keys must be listed in the required array. If a field is optional, its type must be declared as a union with null (e.g., ["string", "null"]).
  3. Unsupported Keywords: Keywords such as patternProperties, minProperties, maxProperties, and format (except for a few like date-time) will trigger instant 400 validation rejections.

3. Resolving with Python and Pydantic v2

If you are generating schemas dynamically using Pydantic in Python, configure your model with extra='forbid' and handle optionals using Optional[T] = None:

from typing import Optional, List
from pydantic import BaseModel, ConfigDict
from openai import OpenAI

class StepAction(BaseModel):
    model_config = ConfigDict(extra="forbid")
    step_number: int
    instruction: str
    target_tool: Optional[str] = None  # Union with null for optionality

class ExecutionPlan(BaseModel):
    model_config = ConfigDict(extra="forbid")
    plan_name: str
    steps: List[StepAction]

client = OpenAI()

completion = client.beta.chat.completions.parse(
    model="gpt-4o-2024-08-06",
    messages=[
        {"role": "system", "content": "Generate a step-by-step developer workflow."},
        {"role": "user", "content": "How do I securely decode a JWT in browser memory?"}
    ],
    response_format=ExecutionPlan,
)

plan = completion.choices[0].message.parsed
print(plan.plan_name)

4. Resolving with TypeScript and Zod / OpenAI SDK

In Node.js or Next.js edge runtimes, use the official zodResponseFormat helper from the openai/helpers/zod package:

import OpenAI from 'openai';
import { z } from 'zod';
import { zodResponseFormat } from 'openai/helpers/zod';

const openai = new OpenAI();

const SecurityAuditSchema = z.object({
  vulnerabilityType: z.string(),
  cweId: z.number(),
  // For optional fields in strict schema mode, use nullable():
  remediationSnippet: z.string().nullable(),
  isExploitableRemotely: z.boolean(),
});

async function runSecurityAudit(sourceCode: string) {
  const response = await openai.beta.chat.completions.parse({
    model: 'gpt-4o-2024-08-06',
    messages: [
      { role: 'system', content: 'Analyze code for cryptographic vulnerabilities.' },
      { role: 'user', content: sourceCode },
    ],
    response_format: zodResponseFormat(SecurityAuditSchema, 'audit_report'),
  });

  return response.choices[0].message.parsed;
}

5. Schema Debugging Checklist

  • Does every object have "additionalProperties": false?
  • Are all property names in the object explicitly included in "required"?
  • Are optional values typed as nullable (type: ["string", "null"])?
  • Have you removed arbitrary constraints like regex patterns that CFG compilers cannot enforce?

Verify your JSON schemas before runtime by checking syntax in our client-side JSON Formatter & Validator.

Advertisement
Put Knowledge Into Practice

Integrated Tools & Learning Resources

DevX Tool combines deep engineering tutorials with client-side interactive tooling so you can test code, verify outputs, and troubleshoot bugs right away.

Recommended Engineering Articles