diff --git a/lib/crewai/src/crewai/tools/base_tool.py b/lib/crewai/src/crewai/tools/base_tool.py index 88c0826a9..0985c590c 100644 --- a/lib/crewai/src/crewai/tools/base_tool.py +++ b/lib/crewai/src/crewai/tools/base_tool.py @@ -18,7 +18,6 @@ from pydantic import ( BaseModel as PydanticBaseModel, ConfigDict, Field, - ValidationError, create_model, field_validator, ) @@ -163,7 +162,7 @@ class BaseTool(BaseModel, ABC): Raises: ValueError: If validation against args_schema fails. """ - if kwargs and self.args_schema is not None and self.args_schema.model_fields: + if self.args_schema is not None and self.args_schema.model_fields: try: validated = self.args_schema.model_validate(kwargs) return validated.model_dump() @@ -178,7 +177,8 @@ class BaseTool(BaseModel, ABC): *args: Any, **kwargs: Any, ) -> Any: - kwargs = self._validate_kwargs(kwargs) + if not args: + kwargs = self._validate_kwargs(kwargs) result = self._run(*args, **kwargs) @@ -203,7 +203,8 @@ class BaseTool(BaseModel, ABC): Returns: The result of the tool execution. """ - kwargs = self._validate_kwargs(kwargs) + if not args: + kwargs = self._validate_kwargs(kwargs) result = await self._arun(*args, **kwargs) self.current_usage_count += 1 return result @@ -356,7 +357,8 @@ class Tool(BaseTool, Generic[P, R]): Returns: The result of the tool execution. """ - kwargs = self._validate_kwargs(kwargs) + if not args: + kwargs = self._validate_kwargs(kwargs) # type: ignore[assignment] result = self.func(*args, **kwargs) @@ -388,7 +390,8 @@ class Tool(BaseTool, Generic[P, R]): Returns: The result of the tool execution. """ - kwargs = self._validate_kwargs(kwargs) + if not args: + kwargs = self._validate_kwargs(kwargs) # type: ignore[assignment] result = await self._arun(*args, **kwargs) self.current_usage_count += 1 return result diff --git a/lib/crewai/tests/tools/test_base_tool.py b/lib/crewai/tests/tools/test_base_tool.py index a9d3a2b6d..8f7ae877b 100644 --- a/lib/crewai/tests/tools/test_base_tool.py +++ b/lib/crewai/tests/tools/test_base_tool.py @@ -268,6 +268,13 @@ class TestBaseToolRunValidation: result = t.run(code="console.log('hi')", language="javascript") assert result == "Executed javascript: console.log('hi')" + def test_run_with_no_args_raises_validation_error(self) -> None: + """Calling run() with no arguments should raise a clear ValueError, + not a cryptic TypeError about missing positional arguments (GH-4611).""" + t = CodeExecutorTool() + with pytest.raises(ValueError, match="validation failed"): + t.run() + def test_run_with_missing_required_kwarg_raises(self) -> None: """Missing required kwargs should raise ValueError from schema validation.""" t = CodeExecutorTool() @@ -378,6 +385,13 @@ class TestBaseToolArunValidation: result = await t.arun(code="print('hello')") assert result == "Async executed python: print('hello')" + @pytest.mark.asyncio + async def test_arun_with_no_args_raises_validation_error(self) -> None: + """Calling arun() with no arguments should raise a clear ValueError (GH-4611).""" + t = AsyncCodeExecutorTool() + with pytest.raises(ValueError, match="validation failed"): + await t.arun() + @pytest.mark.asyncio async def test_arun_with_missing_required_kwarg_raises(self) -> None: """Missing required kwargs should raise ValueError in arun."""