Skip to content

Commit e0f354e

Browse files
joaomdmouraclaude
andcommitted
fix(tools): let raise through the parallel native path, guard all handlers
Chasing down CodeRabbit's note about callers of execute_single_native_tool_call turned up a fifth place this exception was being downgraded: the experimental executor's parallel branch wrapped future.result() in a broad except and folded the abort into a fake tool result, so the remaining parallel calls carried on. The sequential path and crew_agent_executor's parallel branch were already fine. Five separate handlers have swallowed this during review, so added a guard test asserting the passthrough at every site rather than trusting the next one gets spotted. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ETacm2dMASfpMAYUiDu5YG
1 parent 55160c6 commit e0f354e

2 files changed

Lines changed: 30 additions & 0 deletions

File tree

lib/crewai/src/crewai/experimental/agent_executor.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1767,6 +1767,10 @@ def execute_native_tool(
17671767
idx = future_to_idx[future]
17681768
try:
17691769
ordered_results[idx] = future.result()
1770+
except ToolExecutionFailedError:
1771+
# A deliberate stop: folding it into a tool result would
1772+
# let the remaining parallel calls carry on.
1773+
raise
17701774
except Exception as e:
17711775
tool_call = runnable_tool_calls[idx]
17721776
info = extract_tool_call_info(tool_call)

lib/crewai/tests/tools/test_tool_failure.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -700,6 +700,32 @@ def test_crew_ignore_suppresses_recording_end_to_end(self) -> None:
700700
).kickoff()
701701
assert not result.has_tool_failures
702702

703+
def test_every_broad_handler_around_tool_execution_lets_it_through(self) -> None:
704+
"""Guard against a new `except Exception` quietly downgrading an abort.
705+
706+
Five separate handlers have swallowed this exception during review of
707+
this PR, so assert the passthrough at each site rather than trusting
708+
that the next one will be spotted.
709+
"""
710+
import inspect
711+
712+
from crewai.agent.core import Agent as AgentCls
713+
from crewai.agents.crew_agent_executor import CrewAgentExecutor
714+
from crewai.agents.step_executor import StepExecutor
715+
from crewai.experimental.agent_executor import AgentExecutor
716+
717+
sites = [
718+
(AgentCls._execute_with_timeout, "_passthrough_exceptions"),
719+
(StepExecutor.execute, "ToolExecutionFailedError"),
720+
(AgentExecutor.execute_tool_action, "ToolExecutionFailedError"),
721+
(AgentExecutor.execute_native_tool, "ToolExecutionFailedError"),
722+
(CrewAgentExecutor._invoke_loop_react, "ToolExecutionFailedError"),
723+
(CrewAgentExecutor._ainvoke_loop_react, "ToolExecutionFailedError"),
724+
]
725+
for func, expected in sites:
726+
source = inspect.getsource(func)
727+
assert expected in source, f"{func.__qualname__} lost its passthrough"
728+
703729
def test_passthrough_tuple_includes_the_error(self) -> None:
704730
from crewai.agent.core import _passthrough_exceptions
705731

0 commit comments

Comments
 (0)