From e1e8003c243168bb7d090fa8754e03596d3e8ee6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20M=C3=BCller?= <67061560+MagMueller@users.noreply.github.com> Date: Fri, 24 Oct 2025 19:34:18 -0700 Subject: [PATCH] Fix: Ensure direct action calls go through act() for consistent error handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The action_wrapper now delegates to act() instead of calling registry.execute_action() directly. This ensures: - Consistent error handling (returns ActionResult with error, not raw exceptions) - Consistent result normalization (always returns ActionResult) - Full observability (Laminar spans, logging) - Proper handling of BrowserError and TimeoutError 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- browser_use/tools/service.py | 35 +++++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/browser_use/tools/service.py b/browser_use/tools/service.py index a3ac5cd96..61b549854 100644 --- a/browser_use/tools/service.py +++ b/browser_use/tools/service.py @@ -1281,8 +1281,17 @@ Validated Code (after quote fixing): """ # Check if this is a registered action if name in self.registry.registry.actions: - # Create a wrapper that delegates to registry.execute_action + from typing import Union + + from pydantic import create_model + + action = self.registry.registry.actions[name] + + # Create a wrapper that calls act() to ensure consistent error handling and result normalization async def action_wrapper(**kwargs): + # Extract browser_session (required positional argument for act()) + browser_session = kwargs.get('browser_session') + # Separate action params from special params (injected dependencies) special_param_names = { 'browser_session', @@ -1295,12 +1304,26 @@ Validated Code (after quote fixing): # Extract action params (params for the action itself) action_params = {k: v for k, v in kwargs.items() if k not in special_param_names} - # Extract special params (injected dependencies) - special_kwargs = {k: v for k, v in kwargs.items() if k in special_param_names} + # Extract special params (injected dependencies) - exclude browser_session as it's positional + special_kwargs = {k: v for k, v in kwargs.items() if k in special_param_names and k != 'browser_session'} - # Delegate to the existing registry.execute_action method - # This ensures all the logic (sensitive_data, page_url extraction, error handling, etc.) is centralized - return await self.registry.execute_action(action_name=name, params=action_params, **special_kwargs) + # Create the param instance + params_instance = action.param_model(**action_params) + + # Dynamically create an ActionModel with this action + # Use Union for type compatibility with create_model + DynamicActionModel = create_model( + 'DynamicActionModel', + __base__=ActionModel, + **{name: (Union[action.param_model, None], None)}, # type: ignore + ) + + # Create the action model instance + action_model = DynamicActionModel(**{name: params_instance}) + + # Call act() which has all the error handling, result normalization, and observability + # browser_session is passed as positional argument (required by act()) + return await self.act(action=action_model, browser_session=browser_session, **special_kwargs) # type: ignore return action_wrapper