From d9805f499fa11a08901d1801712ae7606b34ed29 Mon Sep 17 00:00:00 2001 From: Deeven Seru Date: Tue, 17 Mar 2026 13:17:38 +0000 Subject: [PATCH 1/2] fix(adk): wrap tool execution errors --- packages/toolbox-adk/src/toolbox_adk/tool.py | 13 +++++-------- packages/toolbox-adk/tests/unit/test_tool.py | 14 ++++++++++++++ 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/packages/toolbox-adk/src/toolbox_adk/tool.py b/packages/toolbox-adk/src/toolbox_adk/tool.py index 720407bfe..491bfba85 100644 --- a/packages/toolbox-adk/src/toolbox_adk/tool.py +++ b/packages/toolbox-adk/src/toolbox_adk/tool.py @@ -266,17 +266,14 @@ async def run_async( "error": f"OAuth2 Credentials required for {self.name}. A consent link has been generated for the user. Do NOT attempt to run this tool again until the user confirms they have logged in." } - result: Optional[Any] = None - error: Optional[Exception] = None - try: # Execute the core tool - result = await self._core_tool(**args) - return result - + return await self._core_tool(**args) except Exception as e: - error = e - raise e + logging.warning( + "Toolbox tool '%s' execution failed: %s", self.name, e, exc_info=True + ) + return {"error": f"{type(e).__name__}: {e}"} finally: if reset_token: USER_TOKEN_CONTEXT_VAR.reset(reset_token) diff --git a/packages/toolbox-adk/tests/unit/test_tool.py b/packages/toolbox-adk/tests/unit/test_tool.py index 0f3c5120d..e4b235520 100644 --- a/packages/toolbox-adk/tests/unit/test_tool.py +++ b/packages/toolbox-adk/tests/unit/test_tool.py @@ -56,6 +56,20 @@ async def test_auth_check_no_token(self): # Should proceed to execute (auth not forced) mock_core.assert_awaited() + @pytest.mark.asyncio + async def test_run_async_returns_error_on_exception(self): + mock_core = AsyncMock(side_effect=RuntimeError("boom")) + mock_core.__name__ = "my_tool" + mock_core.__doc__ = "my description" + + tool = ToolboxTool(mock_core) + ctx = MagicMock() + + result = await tool.run_async({"arg": 1}, ctx) + + assert isinstance(result, dict) and "error" in result + assert "RuntimeError" in result["error"] + @pytest.mark.asyncio async def test_bind_params(self): mock_core = MagicMock() From 6d98da784648972f3222a4ed236350b02b2d99b0 Mon Sep 17 00:00:00 2001 From: Deeven Seru Date: Tue, 17 Mar 2026 13:25:05 +0000 Subject: [PATCH 2/2] fix(adk): mark tool errors as is_error --- packages/toolbox-adk/src/toolbox_adk/tool.py | 2 +- packages/toolbox-adk/tests/unit/test_tool.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/toolbox-adk/src/toolbox_adk/tool.py b/packages/toolbox-adk/src/toolbox_adk/tool.py index 491bfba85..8283496a9 100644 --- a/packages/toolbox-adk/src/toolbox_adk/tool.py +++ b/packages/toolbox-adk/src/toolbox_adk/tool.py @@ -273,7 +273,7 @@ async def run_async( logging.warning( "Toolbox tool '%s' execution failed: %s", self.name, e, exc_info=True ) - return {"error": f"{type(e).__name__}: {e}"} + return {"error": f"{type(e).__name__}: {e}", "is_error": True} finally: if reset_token: USER_TOKEN_CONTEXT_VAR.reset(reset_token) diff --git a/packages/toolbox-adk/tests/unit/test_tool.py b/packages/toolbox-adk/tests/unit/test_tool.py index e4b235520..1bd262ceb 100644 --- a/packages/toolbox-adk/tests/unit/test_tool.py +++ b/packages/toolbox-adk/tests/unit/test_tool.py @@ -68,6 +68,7 @@ async def test_run_async_returns_error_on_exception(self): result = await tool.run_async({"arg": 1}, ctx) assert isinstance(result, dict) and "error" in result + assert result.get("is_error") is True assert "RuntimeError" in result["error"] @pytest.mark.asyncio