Skip to content

fix: preserve partial action results when mid-batch action fails#4770

Merged
sauravpanda merged 4 commits into
browser-use:mainfrom
toller892:fix/preserve-partial-results-on-action-error
May 3, 2026
Merged

fix: preserve partial action results when mid-batch action fails#4770
sauravpanda merged 4 commits into
browser-use:mainfrom
toller892:fix/preserve-partial-results-on-action-error

Conversation

@toller892
Copy link
Copy Markdown
Contributor

@toller892 toller892 commented Apr 30, 2026

Problem

When multi_act() executes a batch of N actions and action K (where 0 < K < N) raises an exception, the partial results from successfully executed actions 1..K-1 are silently lost. The exception propagates through _execute_actions()_handle_step_error(), which creates a single generic ActionResult(error=error_msg) that overwrites any trace of the earlier successes.

The LLM receives only the generic error and loses visibility into which specific actions completed before the failure — even though those actions had real side effects (clicks, form fills, navigation).

Fix

Instead of re-raising the exception, append an error ActionResult to the accumulated results list and return it. This path is already supported downstream:

  • _execute_actions() (line 1205): self.state.last_result is set from the return value
  • _post_process() (lines 1221-1222): explicitly handles multi-action errors via loop detection and replan nudges
  • _finalize(): saves all results to history for the LLM to see

Change: raise eresults.append(ActionResult(error=str(e))); return results

Impact

  • The agent now sees exactly which actions succeeded and which failed
  • No change to the success path
  • Error handling remains correct: single-action errors still increment consecutive_failures, multi-action errors are handled by loop detection

Summary by cubic

Preserves partial results in multi_act() when a mid-batch action fails by returning accumulated successes plus a trailing error ActionResult. Keeps stop/pause and reconnect/shutdown behavior by re-raising InterruptedError and connection-like errors.

  • Bug Fixes
    • For non-control-flow and non-connection exceptions, log, append ActionResult(error="<Exception>: <msg>"), and return accumulated results.
    • Re-raise InterruptedError; asyncio.CancelledError doesn’t reach this handler; re-raise connection-like errors so _handle_step_error can run reconnect/shutdown.
    • Fixed except block indentation to match the inner try (prevented a SyntaxError).

Written for commit 54322e6. Summary will update on new commits.

@CLAassistant
Copy link
Copy Markdown

CLAassistant commented Apr 30, 2026

CLA assistant check
All committers have signed the CLA.

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 1 file

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="browser_use/agent/service.py">

<violation number="1" location="browser_use/agent/service.py:2810">
P1: `multi_act()` now swallows `InterruptedError` from `_check_stop_or_pause()`, so pause/stop control flow no longer propagates to callers and can be treated as a normal action failure.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review, or fix all with cubic.

Comment thread browser_use/agent/service.py Outdated
@PHclaw
Copy link
Copy Markdown

PHclaw commented Apr 30, 2026

Great fix! Preserving partial multi_act() results instead of losing them is the right call. The cubic bot raises a valid concern: InterruptedError from _check_stop_or_pause() will now be swallowed as a regular ActionResult. Suggest explicitly re-raising it:

except Exception as e:
    if isinstance(e, (InterruptedError, asyncio.CancelledError)):
        raise  # Preserve stop/pause control flow
    results.append(ActionResult(error=str(e)))
    return results

Otherwise the core fix is solid - minimal, well-scoped, and the downstream flows already handle the returned error result correctly.

@toller892
Copy link
Copy Markdown
Contributor Author

Good catch, thanks! Added the InterruptedError / CancelledError re-raise guard — pushed in d117d30.

@toller892
Copy link
Copy Markdown
Contributor Author

@cubic-dev-ai the InterruptedError / CancelledError issue was already fixed in d117d30 — they are explicitly re-raised before the ActionResult wrapping. This review appears to be on a stale version.

@cubic-dev-ai
Copy link
Copy Markdown
Contributor

cubic-dev-ai Bot commented Apr 30, 2026

@cubic-dev-ai the InterruptedError / CancelledError issue was already fixed in d117d30 — they are explicitly re-raised before the ActionResult wrapping. This review appears to be on a stale version.

@toller892 You're right—the current code in commit d117d30 already handles this correctly. If I previously flagged that those exceptions would be swallowed, it was based on a stale version of the file before you added the explicit re-raise guard.

The latest diff for browser_use/agent/service.py shows that InterruptedError and asyncio.CancelledError are now caught and re-raised before the generic error wrapping:

# Re-raise control-flow exceptions to preserve stop/pause/interrupt behavior
if isinstance(e, (InterruptedError, asyncio.CancelledError)):
    raise

This correctly preserves the agent's stop and pause behavior while still achieving the goal of returning partial results for standard execution errors.

For more details on how I process updates and manage review history, you can check the Cubic documentation.

When multi_act() executes a batch of actions and one fails mid-way,
the partial results from successfully executed earlier actions were
discarded by re-raising the exception. The agent lost visibility into
which actions completed before the failure.

Return the accumulated results with an error ActionResult appended,
which aligns with the existing post_process logic that explicitly
handles multi-action errors via loop detection and replan nudges
(lines 1221-1222).
PHclaw pointed out that _check_stop_or_pause() raises InterruptedError
inside the multi_act loop. Converting it to an ActionResult would prevent
the agent from properly stopping/pausing between batched actions.

Re-raise InterruptedError and asyncio.CancelledError before wrapping
other exceptions as ActionResult with partial results.
@toller892 toller892 force-pushed the fix/preserve-partial-results-on-action-error branch from d117d30 to fd7eeeb Compare April 30, 2026 08:01
- Re-indent the except block to match the inner try (was a SyntaxError)
- Drop asyncio.CancelledError from the isinstance check (it's BaseException,
  never reaches except Exception)
- Re-raise on _is_connection_like_error so _handle_step_error can run its
  reconnect / browser-closed shutdown logic
- Include exception class name in the preserved ActionResult error
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 1 file (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="browser_use/agent/service.py">

<violation number="1" location="browser_use/agent/service.py:2815">
P2: Re-raising connection-like errors here discards already-succeeded actions in the same batch, because the caller only records `multi_act()` results on normal return and the step error handler replaces them with a single generic error result.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review, or fix all with cubic.

Comment thread browser_use/agent/service.py
@sauravpanda sauravpanda merged commit 8fc4f34 into browser-use:main May 3, 2026
88 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants