Summary
When a tool call inside agentfield.tool_calling.execute_tool_call_loop raises asyncio.TimeoutError, the loop does not terminate or short-circuit. Instead it continues to spend additional turns calling the LLM, which is wasteful (cost + latency) and confusing for debugging.
Where
- File:
sdk/python/agentfield/tool_calling.py
- Function:
execute_tool_call_loop
- Discovered by:
sdk/python/tests/test_tool_calling_error_paths.py::test_tool_execution_timeout_breaks_loop_early (currently skipped with pytest.skip("source bug: tool timeouts do not break the loop early"))
Reproduction
agent.call = AsyncMock(side_effect=asyncio.TimeoutError("tool timed out"))
# ... feed LLM response with one tool call ...
_, trace = await execute_tool_call_loop(
agent=agent,
messages=messages,
tools=[make_tool_schema()],
config=ToolCallConfig(max_turns=3),
...
)
# Expected: make_completion.await_count == 1 (loop bailed after the timeout)
# Actual: loop continues, await_count > 1
Expected behavior
When a tool execution raises asyncio.TimeoutError:
- Record the timeout in the
ToolCallTrace for that call
- Append an error message to
messages describing the timeout
- Break out of the loop (do not call the LLM again) — timeouts indicate a real environmental failure that retrying with the same tool will not fix in the same execution
If we want a configurable behavior (continue vs. break), it should default to break and be opt-out via ToolCallConfig.
Acceptance criteria
Discovered via
PR #352 (test coverage improvements). One of 5 source bugs surfaced while writing failure-mode tests for the Python SDK.
Summary
When a tool call inside
agentfield.tool_calling.execute_tool_call_loopraisesasyncio.TimeoutError, the loop does not terminate or short-circuit. Instead it continues to spend additional turns calling the LLM, which is wasteful (cost + latency) and confusing for debugging.Where
sdk/python/agentfield/tool_calling.pyexecute_tool_call_loopsdk/python/tests/test_tool_calling_error_paths.py::test_tool_execution_timeout_breaks_loop_early(currently skipped withpytest.skip("source bug: tool timeouts do not break the loop early"))Reproduction
Expected behavior
When a tool execution raises
asyncio.TimeoutError:ToolCallTracefor that callmessagesdescribing the timeoutIf we want a configurable behavior (continue vs. break), it should default to break and be opt-out via
ToolCallConfig.Acceptance criteria
asyncio.TimeoutErrorterminates the loop after the current turnmake_completion.await_count == 1in the reproduction abovetest_tool_calling_error_paths.py::test_tool_execution_timeout_breaks_loop_earlyis unskipped and passesDiscovered via
PR #352 (test coverage improvements). One of 5 source bugs surfaced while writing failure-mode tests for the Python SDK.