I’m using OpenAI’s response_format with type: "json_schema" and running into a problem when trying to define a field that should be optional and only included under specific conditions. When "strict": true is enabled, the model throws a 400 BadRequestError if it includes a field not listed in the "required" array, even if that field is valid in other contexts. On the other hand, if I include that field in the "required" array, it forces the model to always return it—even when it’s irrelevant. For example, I have a convo field that should only appear when a contradiction is based on a transcription, but not in all cases. If I include "convo" in "required", it appears in every object, which is undesirable. If I remove it from "required", I get an error when the model tries to return it conditionally. I also tried using constructs like oneOf, anyOf, and if-then-else to make the field conditionally required, but these are not supported by OpenAI’s schema validation. I’m looking for a clean solution or workaround to allow conditional optional fields under "strict": true mode, without triggering errors or forcing unnecessary data into every output.
A strict structured output response format forces the AI to make all the keys provided.
Answer: Create a union of the type of the field and null in the schema.
OpenAI’s own example from documentation (but the style of a function):
{
"name": "get_weather",
"description": "Fetches the weather in the given location",
"strict": true,
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The location to get the weather for"
},
"unit": {
"type": ["string", "null"],
"description": "The unit to return the temperature in",
"enum": ["F", "C"]
}
},
"additionalProperties": false,
"required": [
"location", "unit"
]
}
}
Pydantic as a “structured” response format has a bad habit of making a bunch of anyOf for anything that seems optional within the “required” that is forced on the BaseModel by OpenAI’s response format input of a streamable.
Here’s a method in Python to do the "type | null" union provided in what the AI actually understands, and the API call utilizing it:
from openai import OpenAI
client = OpenAI()
from pydantic import BaseModel, Field, ConfigDict
try:
# 3.11+
from typing import Annotated
except ImportError:
# 3.8–3.10
from typing_extensions import Annotated
from pydantic.json_schema import WithJsonSchema
# Force JSON Schema to use the "type": ["string","null"] style (no anyOf)
StrOrNull = Annotated[
str | None,
WithJsonSchema({"type": ["string", "null"]}, mode="validation")
]
class ExtractedUserLocation(BaseModel):
model_config = ConfigDict(
extra="forbid",
json_schema_extra={
"title": "extracted_user_location",
"description": ("Structured output for an extracted user location.
Any field may be null if unknown."),
},
)
# Required-but-nullable fields
city: StrOrNull = Field(..., description="City name, or null if unknown")
state: StrOrNull = Field(..., description="State/region/province, or null if unknown")
postal_code: StrOrNull = Field(..., description="Postal/ZIP code, or null if unknown")
response = client.responses.parse(
model="gpt-5-mini",
input=[
{"role": "system", "content": "Extract the user's location."},
{"role": "user", "content": "I'm glad to help keep Austin weird - BBQ or Falafel."},
],
text_format=ExtractedUserLocation,
)
print(response.output_parsed)
And the resulting schema, as an example of what you can send otherwise as a programming language data object or part of a REST API call:
>>> import json
>>> print(json.dumps(ExtractedUserLocation.model_json_schema(), indent=2))
{
"additionalProperties": false,
"description": "Structured output for an extracted user location. Any field may be null if unknown.",
"properties": {
"city": {
"description": "City name, or null if unknown",
"title": "City",
"type": [
"string",
"null"
]
},
"state": {
"description": "State/region/province, or null if unknown",
"title": "State",
"type": [
"string",
"null"
]
},
"postal_code": {
"description": "Postal/ZIP code, or null if unknown",
"title": "Postal Code",
"type": [
"string",
"null"
]
}
},
"required": [
"city",
"state",
"postal_code"
],
"title": "extracted_user_location",
"type": "object"
}