You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Have you searched for related issues? Yes. #3286 fixed the same class of problem for the unknown-tool (else) branch. This issue reports the same problem for the known-tool exception (if) branch, which remains unfixed.
Describe the bug
PR #3287 correctly added RealtimeModelSendToolOutput to the unknown-tool (else) branch in _handle_tool_call(). However, the known-tool (if) branch at line 697 calls await invoke_function_tool(...) without a try/except. When the tool raises an exception (e.g. ToolTimeoutError with timeout_behavior="raise_exception", or a user-raised ValueError), the exception propagates out of _handle_tool_call, is caught by _on_tool_call_task_done, logged, and a local RealtimeError is emitted — but no RealtimeModelSendToolOutput is sent back to the model.
This means the model waits indefinitely for a function call result that will never arrive, until the server-side timeout fires (~30 seconds). During this time the session appears to hang.
Related paths checked:
Known tool call success: sends RealtimeModelSendToolOutput(start_response=True) ✓
Tool call rejection: sends _send_tool_rejection() with model-visible output ✓
Handoff tool call: sends tool output after session update ✓
Known tool call failure (exception/timeout): no RealtimeModelSendToolOutput sent ✗
The existing test at test_session.py:1199 (assert len(mock_model.sent_tool_outputs) == 0) inadvertently codifies this missing-output behavior as expected.
ValueError: tool failed
Model received tool output: False
Local error emitted: Tool call task failed: tool failed...
Expected behavior
When a known function tool invocation raises an exception (including ToolTimeoutError), the session should send RealtimeModelSendToolOutput with an error message and start_response=True back to the model — just as it already does for unknown tools (#3287). The local RealtimeError event should continue to be emitted for application observers.
The fix should also cover the handoff branch (elif event.name in handoff_map, line 720) where handoff.on_invoke_handoff may similarly raise.
Please read this first
else) branch. This issue reports the same problem for the known-tool exception (if) branch, which remains unfixed.Describe the bug
PR #3287 correctly added
RealtimeModelSendToolOutputto the unknown-tool (else) branch in_handle_tool_call(). However, the known-tool (if) branch at line 697 callsawait invoke_function_tool(...)without atry/except. When the tool raises an exception (e.g.ToolTimeoutErrorwithtimeout_behavior="raise_exception", or a user-raisedValueError), the exception propagates out of_handle_tool_call, is caught by_on_tool_call_task_done, logged, and a localRealtimeErroris emitted — but noRealtimeModelSendToolOutputis sent back to the model.This means the model waits indefinitely for a function call result that will never arrive, until the server-side timeout fires (~30 seconds). During this time the session appears to hang.
Related paths checked:
RealtimeModelSendToolOutput(start_response=True)✓_send_tool_rejection()with model-visible output ✓RealtimeModelSendToolOutputwith error message (fixed by fix: #3286 send realtime output for unknown tool calls #3287) ✓RealtimeModelSendToolOutputsent ✗The existing test at
test_session.py:1199(assert len(mock_model.sent_tool_outputs) == 0) inadvertently codifies this missing-output behavior as expected.Debug information
v0.17.1Repro steps
Run this script:
Actual output:
Expected behavior
When a known function tool invocation raises an exception (including ToolTimeoutError), the session should send RealtimeModelSendToolOutput with an error message and start_response=True back to the model — just as it already does for unknown tools (#3287). The local RealtimeError event should continue to be emitted for application observers.
The fix should also cover the handoff branch (elif event.name in handoff_map, line 720) where handoff.on_invoke_handoff may similarly raise.