Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

function_tool rejects valid arguments when a parameter uses Pydantic strict mode (Enum, date, UUID)

Open
#5,448 0 comments 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

Nobody has claimed this yet.

Assessment

Difficulty
3/5
Estimated time
1-2 days
Newbie friendliness
65/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
python
Domain
api, backend

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_type and handoff input validation strict when strict_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 main at 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

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from openai/openai-agents-python

All issues in openai/openai-agents-python

Similar issues

More Python issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.