Skip to content

[BUG] Bare raise in ToolUsage._original_tool_calling causes RuntimeError instead of ToolUsageError #6430

Description

@molinto

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

  1. 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)

  2. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions