Skip to content

fix: send tool output to model when function tool raises exception#3373

Draft
bbiiggjjuu wants to merge 4 commits into
openai:mainfrom
bbiiggjjuu:fix/realtime-tool-failure-output
Draft

fix: send tool output to model when function tool raises exception#3373
bbiiggjjuu wants to merge 4 commits into
openai:mainfrom
bbiiggjjuu:fix/realtime-tool-failure-output

Conversation

@bbiiggjjuu
Copy link
Copy Markdown

@bbiiggjjuu bbiiggjjuu commented May 12, 2026

Summary

When a known function tool invocation raises an exception (e.g. ToolTimeoutError
with timeout_behavior="raise_exception", or user-raised ValueError), the session
now sends RealtimeModelSendToolOutput with an error message and start_response=True
back to the model before re-raising the exception.

This mirrors the behavior introduced for unknown tool calls in #3287, and ensures
the model is not left waiting for a function call result that will never arrive.

The same protection is also applied to the handoff branch where
handoff.on_invoke_handoff may raise.

Tests

  • make format ,make lint,'make tests' pass
  • mypy and pyright pass on changed files
  • uv run pytest tests/realtime/test_session.py -k "tool_timeout or tool_exception" -v — 5/5 pass
  • make tests — 4524 passed.
    The single failure (tests/test_trace_processor.py::test_tracing_atexit_cleanup_timeout_preserves_process_exit_code_on_504)
    is a pre-existing environment-specific test unrelated to this change: it spawns a child process that exercises httpx retry behavior during trace export, and the child process timed out under subprocess.run(timeout=3.0) in this environment.
    It belongs to the tracing subsystem (tests/test_trace_processor.py) and is not affected by changes to src/agents/realtime/session.py ortests/realtime/test_session.py,and even if I haven’t modified the code, this test doesn’t run on my local environment.

Issue number

Closes #3356

Checks

  • [] I've added new tests (if relevant)
  • [] I've added/updated the relevant documentation
  • I've run make lint and make format
  • I've made sure tests pass

Copilot AI review requested due to automatic review settings May 12, 2026 02:53
@github-actions github-actions Bot added bug Something isn't working feature:realtime labels May 12, 2026
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR ensures realtime sessions don’t leave the model waiting indefinitely when a known function tool call (or a handoff tool call) raises an exception. It mirrors the “unknown tool” handling added in #3287 by sending a RealtimeModelSendToolOutput(start_response=True) to the model before re-raising the exception.

Changes:

  • Wrap known function tool invocation in a try/except and send model-visible error tool output before re-raising.
  • Apply the same protection to the handoff invocation path.
  • Update session tests to expect a tool output to be sent when timeouts/exceptions are raised.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 5 comments.

File Description
src/agents/realtime/session.py Sends a model-visible tool output when a known tool/handoff raises, before re-raising.
tests/realtime/test_session.py Updates expectations to assert a tool output is sent for raised timeouts/exceptions.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +703 to +707
except Exception:
await self._model.send_event(
RealtimeModelSendToolOutput(
tool_call=event,
output=f"Tool '{event.name}' failed",
Comment on lines +744 to +748
except Exception:
await self._model.send_event(
RealtimeModelSendToolOutput(
tool_call=event,
output=f"Handoff '{event.name}' failed",
await session._handle_tool_call(tool_call_event)

assert len(mock_model.sent_tool_outputs) == 0
assert len(mock_model.sent_tool_outputs) == 1
assert isinstance(session._stored_exception, ToolTimeoutError)
assert session._stored_exception.tool_name == "slow_tool"
assert len(mock_model.sent_tool_outputs) == 0
assert len(mock_model.sent_tool_outputs) == 1
# But no tool output should have been sent and no end event queued
assert len(mock_model.sent_tool_outputs) == 0
# An error tool output should be sent to the model before re-raising
assert len(mock_model.sent_tool_outputs) == 1
@seratch seratch removed the bug Something isn't working label May 12, 2026
@seratch seratch marked this pull request as draft May 12, 2026 05:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Realtime known tool failures (exception/timeout) do not send model-visible output

3 participants