diff --git a/src/agents/function_schema.py b/src/agents/function_schema.py index 8fe52df320..93e5deccc3 100644 --- a/src/agents/function_schema.py +++ b/src/agents/function_schema.py @@ -318,7 +318,19 @@ def function_schema( # field_name -> (type_annotation, default_value_or_Field(...)) fields: dict[str, Any] = {} + # Pydantic's create_model() treats certain names as model-level configuration + # keys rather than fields (e.g. 'model_config', 'model_fields'). Passing + # a FieldInfo for those names causes a TypeError deep inside Pydantic, so + # we reject them early with a clear message. + _PYDANTIC_RESERVED_NAMES = {"model_config", "model_fields", "model_computed_fields"} + for name, param in filtered_params: + if name in _PYDANTIC_RESERVED_NAMES: + raise UserError( + f"Function '{func.__name__}' has a parameter named '{name}', which is reserved " + f"by Pydantic and cannot be used as a tool parameter name. " + f"Please rename the parameter." + ) ann = type_hints.get(name, param.annotation) default = param.default diff --git a/tests/test_function_schema.py b/tests/test_function_schema.py index 9771bda99d..f749de2166 100644 --- a/tests/test_function_schema.py +++ b/tests/test_function_schema.py @@ -885,3 +885,13 @@ def func_with_annotated_multiple_field_constraints( with pytest.raises(ValidationError): # zero factor fs.params_pydantic_model(**{"score": 50, "factor": 0.0}) + + +def test_pydantic_reserved_param_name_raises_user_error(): + """Parameters named 'model_config' (or other Pydantic reserved names) must raise UserError.""" + + def func_with_reserved(model_config: str) -> str: + return model_config + + with pytest.raises(UserError, match="reserved by Pydantic"): + function_schema(func_with_reserved)