function_tool rejects valid arguments when a parameter uses Pydantic strict mode (Enum, date, UUID)
Maintainers usually reply within 1 day
Nobody has claimed this yet.
Assessment
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Newbie friendliness
- 65/100
Research direction
Start with _prepare_arguments and validation in src/agents/tool.py, then inspect parameter validation in src/agents/agent.py for Agent.as_tool. The issue suggests strict Pydantic cases to cover: Enum, date, UUID and strict dataclasses, plus a strict int that must still reject a string. Done when valid JSON arguments reach both tools and the invalid strict int remains rejected.
Written by the indexing model from the issue text.
Description
Please read this first
- Have you read the docs? Yes
- Have you searched for related issues? Yes. I found nothing on tool argument validation. The closest is #3724, which made
output_typeand handoff input validation strict whenstrict_json_schema=True.
Describe the bug
When a @function_tool parameter uses Pydantic strict mode (ConfigDict(strict=True), Field(strict=True) or Strict()), arguments that match the tool's own params_json_schema are rejected for Enum, date, datetime, UUID and tuple fields. The tool never runs, and the model only gets "An error occurred while running the tool. Please try again." The same model and the same JSON work as an output_type and as a handoff input_type. This is Pydantic's strictness, not the SDK's strict_mode option.
_prepare_arguments decodes the JSON arguments with json.loads (tool.py#L1967) and validates the dict with schema.params_pydantic_model(**validation_input) (tool.py#L2787-L2791), which is Pydantic's Python mode. In strict mode, Python mode only accepts real Enum, date or UUID instances, while JSON mode also accepts the strings the schema asks for. The Pydantic docs say "strict mode is looser when validating from JSON". output_type and handoff inputs are validated in JSON mode (agent_output.py#L148-L153, handoffs/__init__.py#L358-L364), so the same model works there.
Agent.as_tool(parameters=...) fails the same way, because it calls params_adapter.validate_python(json_data) (agent.py#L761). A strict Pydantic dataclass fails at both call sites even when it only has str and int fields (dataclass_exact_type).
Debug information
- Agents SDK version: 0.24.0, and
mainat cd26bbbc - Related library versions: pydantic 2.12.3, pydantic-core 2.41.4
- Python version: 3.12.7
- Operating system: macOS 26.6.2
- Model and model provider: none, offline repro with
agents.testing.ScriptedModel - Does the issue reproduce with the latest Agents SDK release? Yes
- Does the issue occur consistently or intermittently? Consistently
Output of OPENAI_AGENTS_DISABLE_TRACING=1 python repro.py with the script below, the same on 0.24.0 and main. tool ran: is never printed.
model got: ['An error occurred while running the tool. Please try again.']
output_type: priority=<Priority.HIGH: 'high'> due=datetime.date(2026, 11, 1)
By default the error only says Invalid JSON input for tool create_ticket, though the JSON is valid. With OPENAI_AGENTS_DONT_LOG_TOOL_DATA=0 the SDK logs the Pydantic errors: Input should be an instance of Priority [type=is_instance_of, input_value='high', input_type=str] for ticket.priority and Input should be a valid date [type=date_type, input_value='2026-11-01', input_type=str] for ticket.due.
Repro steps
import asyncio
import datetime
import enum
import json
from pydantic import BaseModel, ConfigDict
from agents import Agent, Runner, function_tool
from agents.testing import ScriptedModel, assistant_message, function_call
class Priority(enum.Enum):
LOW = "low"
HIGH = "high"
class Ticket(BaseModel):
model_config = ConfigDict(strict=True)
priority: Priority
due: datetime.date
@function_tool
def create_ticket(ticket: Ticket) -> str:
print("tool ran:", ticket)
return "created"
ticket = {"priority": "high", "due": "2026-11-01"}
async def main() -> None:
call = function_call("create_ticket", {"ticket": ticket}, call_id="c1")
model = ScriptedModel([[call], [assistant_message("ok")]])
result = await Runner.run(Agent(name="a", model=model, tools=[create_ticket]), "file it")
outputs = [i.output for i in result.new_items if i.type == "tool_call_output_item"]
print("model got:", outputs)
# The same model accepts the same JSON as an output_type.
model = ScriptedModel([[assistant_message(json.dumps(ticket))]])
result = await Runner.run(Agent(name="b", model=model, output_type=Ticket), "x")
print("output_type:", result.final_output)
asyncio.run(main())
Expected behavior
Arguments that match the tool's schema validate and the tool runs, as with output_type.
Suggested fix
Keep the Python-mode call and retry in JSON mode only when it raises ValidationError. In _create_function_tool, next to _prepare_arguments:
def _validate_arguments(data: dict[str, Any]) -> BaseModel:
try:
return (
schema.params_pydantic_model(**data) if data else schema.params_pydantic_model()
)
except ValidationError:
pass
# Tool arguments are JSON. In strict mode, Python mode rejects the JSON form of
# Enum, date, UUID or tuple values, so retry in JSON mode.
return schema.params_pydantic_model.model_validate_json(json.dumps(data))
Then parsed = _validate_arguments(validation_input) replaces L2787-L2791. as_tool needs the same pattern at agent.py#L761, retrying with params_adapter.validate_json(json.dumps(data)) (agent.py doesn't import json yet). With both changes the repro runs the tool (model got: ['created']), a strict int still rejects "5", and the test suite gives the same results as on main. Calls that validate today never reach the retry, so they don't change.
Validating in JSON mode every time would also fix this, but it changes what some existing non-strict tools receive. With pydantic 2.12.3, a Path | str parameter gets PosixPath('notes.txt') instead of 'notes.txt', and a Decimal parameter sent 5.0 gets Decimal('5') instead of Decimal('5.0'). That's why I'd go with the fallback, but either works for the strict cases.
For tests, I'd cover only the strict cases: a strict model with Enum, date and UUID fields and a strict dataclass, through @function_tool and as_tool(parameters=...), plus a check that a strict int still rejects "5".
- Dominant language
- Python
- Stars
- 30.4k
- Forks
- 4.9k
- Avg merge
- 14h 33m
- Merged PRs (30d)
- 142
Getting set up
- No Dockerfile or Docker Compose file
- Has a pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from openai/openai-agents-python
-
fix(typing): explicitly specialize ToolContext[Any] in ToolFunctionWithToolContextMay be free again @AbarnaaSree claimed this 11 days ago, and no pull request is open. Open
Difficulty 1/5 Under an hour Newbie friendliness 85/100
openai/openai-agents-python#5387 · 4 comments ·
Maintainers usually reply within 1 day
-
Difficulty 3/5 1-2 days Newbie friendliness 65/100
openai/openai-agents-python#5447 ·
Maintainers usually reply within 1 day
-
function_tool on a callable object uses an inherited or generated class docstring as the tool descriptionPossibly taken @Narendra02053 claimed this 2 days ago. Open
Difficulty 3/5 1-2 days Newbie friendliness 58/100
openai/openai-agents-python#5443 · 2 comments ·
Maintainers usually reply within 1 day
-
Difficulty 3/5 1-2 days Newbie friendliness 70/100
openai/openai-agents-python#5441 · 1 comment ·
Maintainers usually reply within 1 day
-
Difficulty 4/5 3-5 days Newbie friendliness 44/100
openai/openai-agents-python#5416 ·
Maintainers usually reply within 1 day
All issues in openai/openai-agents-python
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
ComposioHQ/awesome-claude-skills#2398 ·
Maintainers usually reply within 2 days
-
bug converter
Difficulty 2/5 1-3 hours Newbie friendliness 62/100
grafana/sigma-rule-deployment#435 ·
Maintainers usually reply within 1 day
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 73/100
datamodel-code-generator/datamodel-code-generator#5589 ·
Maintainers usually reply within 1 day
-
Shell: serve the home app's Cookie Policy on a hard reloadPossibly taken @2001anshkaushik claimed this today. Openfeature module:ai
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
rocketride-org/rocketride-server#2899 · 1 comment ·
Maintainers usually reply within 2 days
-
GEFSv12
Difficulty 2/5 1-3 hours Newbie friendliness 70/100