Skip to content

Widen RunHooks.on_tool_end result type to Any#3517

Closed
NishchayMahor wants to merge 1 commit into
openai:mainfrom
NishchayMahor:fix/on-tool-end-result-type-3512
Closed

Widen RunHooks.on_tool_end result type to Any#3517
NishchayMahor wants to merge 1 commit into
openai:mainfrom
NishchayMahor:fix/on-tool-end-result-type-3512

Conversation

@NishchayMahor
Copy link
Copy Markdown

Closes #3512.

Summary

Tools can return any JSON-serialisable value — strings, Pydantic models, dicts, lists. The runtime forwards whatever the tool returned (after output guardrails) straight into `hooks.on_tool_end`:

```python

src/agents/run_internal/tool_execution.py

final_result = await _execute_tool_output_guardrails(...) # -> Any
self.hooks.on_tool_end(tool_context, self.public_agent, func_tool, final_result)
```

But `RunHooksBase.on_tool_end` and `AgentHooksBase.on_tool_end` declared `result: str`. With strict type checking that triggers:

```
TypeError: Argument 'result' has incorrect type (expected str, got dict)
```

The fix is to align the hook signature with what the runtime actually passes — widen `result` to `Any` and document what the parameter contains.

What changed

  • `src/agents/lifecycle.py` — `RunHooksBase.on_tool_end` and `AgentHooksBase.on_tool_end` now take `result: Any`, with a docstring note that this is the raw post-guardrail value the tool returned.
  • `tests/test_run_hooks.py` — new `test_on_tool_end_receives_structured_dict_result` regression that runs a `function_tool` returning `dict[str, Any]` and asserts the hook sees the raw dict.

No call sites changed shape — they were already passing through the raw value.

Compatibility

Backward-compatible widening. Existing overrides that typed the parameter `str` keep working because `str` is a subtype of `Any`; the only behavioural change is that strict runtime type checks now accept the dict/list/Pydantic-model values the runtime was already passing in.

Verification

```
$ uv run pytest tests/test_run_hooks.py tests/test_global_hooks.py tests/test_agent_llm_hooks.py -q
21 passed in 0.18s

$ uv run ruff check src/agents/lifecycle.py tests/test_run_hooks.py
All checks passed!

$ uv run pyright src/agents/lifecycle.py tests/test_run_hooks.py
0 errors, 0 warnings, 0 informations
```

Tools can return any JSON-serialisable value — strings, dicts, Pydantic
models, lists. The hook signature claimed str, but the runtime forwards
whatever the tool returned (after output guardrails), so any function
tool emitting structured output trips the contract once strict typing
gets involved.

Change RunHooksBase.on_tool_end and AgentHooksBase.on_tool_end to take
result: Any and document what callers actually receive. No call sites
change shape — they were already passing through the raw value.

Closes openai#3512
@seratch seratch added the duplicate This issue or pull request already exists label May 28, 2026
@seratch
Copy link
Copy Markdown
Member

seratch commented May 28, 2026

Thanks for sharing this. Resolved by #3518

@seratch seratch closed this May 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

duplicate This issue or pull request already exists

Projects

None yet

Development

Successfully merging this pull request may close these issues.

RunHooksBase on_tool_end result type error

2 participants