ensure_strict_json_schema() only strips the default key from a property when its value is exactly None:
# strip `None` defaults as there's no meaningful distinction here
# the schema will still be `nullable` and the model will default
# to using `None` anyway
if json_schema.get("default", NOT_GIVEN) is None:
json_schema.pop("default")
Any field with a non-null default (e.g. an enum, a Decimal, an int, a plain string) keeps its default key in the generated strict schema. OpenAI's Structured Outputs API rejects that schema at call time with:
'default' is not permitted within a property definition
This forces users to disable strict_mode entirely for any tool whose Pydantic model has a field with a non-null default, losing the schema guarantees strict mode is meant to provide.
Repro
from decimal import Decimal
from enum import Enum
from pydantic import BaseModel
from agents import function_tool
class Currency(str, Enum):
EUR = "EUR"
USD = "USD"
class Invoice(BaseModel):
total: Decimal
currency: Currency = Currency.EUR # non-null default
@function_tool
def create_invoice(data: Invoice):
...
# create_invoice.params_json_schema still contains
# {"currency": {..., "default": "EUR"}}
# -> OpenAI API call fails with:
# "'default' is not permitted within a property definition"
Expected behavior
default should be stripped from every property in the strict schema, not just when it's None.
Why this is safe to fix unconditionally
A few lines above in the same function, every property is already forced into required:
json_schema["required"] = list(properties.keys())
That's how strict mode simulates an "optional" field — it stays required, but its type allows null. Once a field is required, the model must always return an explicit value, so the Python-level default is never actually used by the API. It's dead weight in a strict schema regardless of whether its value is None or something else — the current is None check is an arbitrary distinction that shouldn't exist.
Suggested fix
# strip ALL defaults, not just `None`: since every property is marked
# `required` above, the model must always supply an explicit value for
# the field regardless of any Python-level default — so `default` is
# dead weight in a strict schema, and the API rejects it outright when
# non-null.
json_schema.pop("default", None)
(The NOT_GIVEN import becomes unused after this change if nothing else in the file relies on it.)
|
if json_schema.get("default", NOT_GIVEN) is None: |
ensure_strict_json_schema()only strips thedefaultkey from a property when its value is exactlyNone:Any field with a non-null default (e.g. an enum, a
Decimal, anint, a plain string) keeps itsdefaultkey in the generated strict schema. OpenAI's Structured Outputs API rejects that schema at call time with:This forces users to disable
strict_modeentirely for any tool whose Pydantic model has a field with a non-null default, losing the schema guarantees strict mode is meant to provide.Repro
Expected behavior
defaultshould be stripped from every property in the strict schema, not just when it'sNone.Why this is safe to fix unconditionally
A few lines above in the same function, every property is already forced into
required:That's how strict mode simulates an "optional" field — it stays required, but its type allows
null. Once a field is required, the model must always return an explicit value, so the Python-leveldefaultis never actually used by the API. It's dead weight in a strict schema regardless of whether its value isNoneor something else — the currentis Nonecheck is an arbitrary distinction that shouldn't exist.Suggested fix
(The
NOT_GIVENimport becomes unused after this change if nothing else in the file relies on it.)openai-agents-python/src/agents/strict_schema.py
Line 367 in 1816d2e