Description
ToolUsage._original_tool_calling() in lib/crewai/src/crewai/tools/tool_usage.py (~line 853) contains a bare raise outside any except block:
if not isinstance(arguments, dict):
if raise_error:
raise # <- no active exception here
return ToolUsageError(f"{I18N_DEFAULT.errors('tool_arguments_error')}")
A bare raise re-raises the exception currently being handled, but on this path no exception is active (the preceding try/except completed normally). So if _validate_tool_input ever returns a non-dict, Python raises RuntimeError: No active exception to re-raise instead of the intended tool-arguments error.
Today _validate_tool_input always returns a dict or raises, so this is a latent landmine rather than a hot path — but _tool_calling() calls this with raise_error=True and relies on catching a meaningful exception to trigger its fallback, so any future change or subclass would surface this as a confusing RuntimeError.
ruff check --select PLE0704 also flags this line. (It flags one more bare raise in crewai/agent/core.py:660; that one is only ever reached from within an exception context, so it behaves correctly at runtime.)
This issue was written with AI assistance — per CONTRIBUTING.md it needs the llm-generated label, which I can't set myself; could a maintainer please add it?
Steps to Reproduce
-
Force _original_tool_calling down the non-dict branch with raise_error=True (e.g. patch _validate_tool_input to return a list):
from unittest.mock import MagicMock, patch
from crewai.tools import tool
from crewai.tools.tool_usage import ToolUsage
@tool("dummy_tool")
def dummy_tool(x: str) -> str:
"""A dummy tool."""
return x
action = MagicMock()
action.tool = "dummy_tool"
action.tool_input = '["not", "a", "dict"]'
tu = ToolUsage(tools_handler=MagicMock(), tools=[dummy_tool], task=MagicMock(),
function_calling_llm=None, agent=MagicMock(), action=action)
with patch.object(tu, "_select_tool", return_value=dummy_tool),
patch.object(tu, "_validate_tool_input", return_value=["not", "a", "dict"]):
tu._original_tool_calling(action.tool_input, raise_error=True)
-
Observe: RuntimeError: No active exception to re-raise
Expected behavior
A meaningful exception is raised — e.g. ToolUsageError with the existing i18n message ("Error: the Action Input is not a valid key, value dictionary."), matching the raise_error=False branch which returns exactly that.
Screenshots/Code snippets
Suggested fix:
if not isinstance(arguments, dict):
if raise_error:
raise ToolUsageError(f"{I18N_DEFAULT.errors('tool_arguments_error')}")
return ToolUsageError(f"{I18N_DEFAULT.errors('tool_arguments_error')}")
I have a fix with a regression test ready and will open a PR referencing this issue.
Operating System
Windows 11
Python Version
3.12
crewAI Version
1.15.2a2
crewAI Tools Version
ot installed / N/A — bug is in the core crewai packag
Virtual Environment
Venv
Evidence
Running the reproduction above produces:
Traceback (most recent call last):
...
File "crewai/tools/tool_usage.py", line 853, in _original_tool_calling
raise
RuntimeError: No active exception to re-raise
Also flagged by static analysis:
$ ruff check lib/crewai/src/crewai --select PLE0704
PLE0704 Bare `raise` statement is not inside an exception handler
--> crewai/tools/tool_usage.py:853
After the suggested fix, the same reproduction raises:
ToolUsageError: Error: the Action Input is not a valid key, value dictionary.
Possible Solution
Replace the bare raise with a meaningful exception, mirroring the raise_error=False branch (ToolUsageError is already an Exception subclass):
if not isinstance(arguments, dict):
if raise_error:
raise ToolUsageError(f"{I18N_DEFAULT.errors('tool_arguments_error')}")
return ToolUsageError(f"{I18N_DEFAULT.errors('tool_arguments_error')}")
I have this fix with a regression test ready and will open a PR referencing this issue.
Additional context
This issue was written with AI assistance — per CONTRIBUTING.md it requires the llm-generated label. I don't have permission to add labels, so could a maintainer please apply it?
The same ruff rule flags one other bare raise in crewai/agent/core.py:660, but that one is only ever reached from within an exception context (via _handle_execution_error), so it behaves correctly at runtime — noting it here for completeness, not proposing a change.
Description
ToolUsage._original_tool_calling()inlib/crewai/src/crewai/tools/tool_usage.py(~line 853) contains a bareraiseoutside anyexceptblock:A bare
raisere-raises the exception currently being handled, but on this path no exception is active (the preceding try/except completed normally). So if_validate_tool_inputever returns a non-dict, Python raisesRuntimeError: No active exception to re-raiseinstead of the intended tool-arguments error.Today
_validate_tool_inputalways returns a dict or raises, so this is a latent landmine rather than a hot path — but_tool_calling()calls this withraise_error=Trueand relies on catching a meaningful exception to trigger its fallback, so any future change or subclass would surface this as a confusing RuntimeError.ruff check --select PLE0704also flags this line. (It flags one more bareraiseincrewai/agent/core.py:660; that one is only ever reached from within an exception context, so it behaves correctly at runtime.)This issue was written with AI assistance — per CONTRIBUTING.md it needs the
llm-generatedlabel, which I can't set myself; could a maintainer please add it?Steps to Reproduce
Force
_original_tool_callingdown the non-dict branch withraise_error=True(e.g. patch_validate_tool_inputto return a list):from unittest.mock import MagicMock, patch
from crewai.tools import tool
from crewai.tools.tool_usage import ToolUsage
@tool("dummy_tool")
def dummy_tool(x: str) -> str:
"""A dummy tool."""
return x
action = MagicMock()
action.tool = "dummy_tool"
action.tool_input = '["not", "a", "dict"]'
tu = ToolUsage(tools_handler=MagicMock(), tools=[dummy_tool], task=MagicMock(),
function_calling_llm=None, agent=MagicMock(), action=action)
with patch.object(tu, "_select_tool", return_value=dummy_tool),
patch.object(tu, "_validate_tool_input", return_value=["not", "a", "dict"]):
tu._original_tool_calling(action.tool_input, raise_error=True)
Observe:
RuntimeError: No active exception to re-raiseExpected behavior
A meaningful exception is raised — e.g. ToolUsageError with the existing i18n message ("Error: the Action Input is not a valid key, value dictionary."), matching the raise_error=False branch which returns exactly that.
Screenshots/Code snippets
Suggested fix:
I have a fix with a regression test ready and will open a PR referencing this issue.
Operating System
Windows 11
Python Version
3.12
crewAI Version
1.15.2a2
crewAI Tools Version
ot installed / N/A — bug is in the core crewai packag
Virtual Environment
Venv
Evidence
Running the reproduction above produces:
Also flagged by static analysis:
After the suggested fix, the same reproduction raises:
Possible Solution
Replace the bare
raisewith a meaningful exception, mirroring the raise_error=False branch (ToolUsageError is already an Exception subclass):I have this fix with a regression test ready and will open a PR referencing this issue.
Additional context
This issue was written with AI assistance — per CONTRIBUTING.md it requires the
llm-generatedlabel. I don't have permission to add labels, so could a maintainer please apply it?The same ruff rule flags one other bare
raiseincrewai/agent/core.py:660, but that one is only ever reached from within an exception context (via_handle_execution_error), so it behaves correctly at runtime — noting it here for completeness, not proposing a change.