fix: validate tool kwargs even when empty to prevent cryptic TypeError (#4611)

This commit is contained in:
Greyson LaLonde
2026-02-26 16:18:03 -05:00
committed by GitHub
parent 373abbb6b7
commit c4a328c9d5
2 changed files with 23 additions and 6 deletions

View File

@@ -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

View File

@@ -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."""