fix: split Desktop Agent OS errors into recoverable vs unfixable - #307
Merged
Merged
Conversation
Reading a remote file or directory that does not exist raised a DesktopAgentOsError that inherited from BaseException, so the tool-calling loop's `except Exception` handler never caught it. The error propagated all the way up and crashed the run instead of being surfaced to the agent as a tool error result, as recoverable tool failures are everywhere else. Introduce two error types that map onto the existing fatal/recoverable handling in the tool-calling loop: - DesktopAgentOsException (recoverable): a plain Exception for failures the agent can work around (path not found, undecodable file contents). The loop's generic `except Exception` catches it and returns it to the agent. - DesktopAgentOsError (unfixable): now inherits from AutomationError for protocol violations (unexpected response type, missing error and response). The loop's existing `except (AgentError, AutomationError): raise` re-raises it, terminating the run cleanly instead of crashing on an uncaught BaseException. Controller operation failures (res.error) and undecodable payloads now raise the recoverable type; contract violations keep the fatal type. No changes to the generic tool-calling loop are required. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
mlikasam-askui
approved these changes
Aug 13, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
When a tool that talks to the Desktop Agent OS fails — for example, reading a remote file or listing a directory that does not exist — the whole agent run crashed with an unhandled error, instead of the failure being reported back to the agent as a tool error (the way other tool failures are handled).
The reason: the Desktop Agent OS error type inherited from
BaseException. The tool-calling loop catches tool failures withexcept Exceptionand converts them into an error result the agent can react to, butexcept Exceptiondoes not catchBaseExceptionsubclasses, so the error slipped past and propagated all the way up.Approach
Rather than simply reparenting the single error type (see the discussion on the previous attempt), this splits Desktop Agent OS errors into two types, matching how the codebase already distinguishes fatal from recoverable errors in the tool-calling loop:
DesktopAgentOsException(recoverable) — a plainExceptionfor failures the agent can react to and work around (e.g. a path that does not exist, or file contents that cannot be decoded). The loop's genericexcept Exceptioncatches it and surfaces it to the agent as a tool error result, so the run continues.DesktopAgentOsError(unfixable) — now inherits fromAutomationError, for protocol violations (unexpected response type, a response missing both an error and a payload). The loop's existingexcept (AgentError, AutomationError): raisere-raises it, terminating the run cleanly instead of crashing on an uncaughtBaseException.Controller-reported operation failures and undecodable payloads now raise the recoverable type; contract/protocol violations keep the fatal type. No changes to the generic tool-calling loop are required — both types plug into the existing fatal/recoverable handling.
DesktopAgentOsErrorsubclassesAutomationError(frommodels/exceptions.py) rather thanAgentError; the latter would introduce a circular import, while the former is cycle-safe.Testing
is_error=Truetool result, the fatal one propagates.pdm run qa:fixpasses (typecheck, format, lint); affected unit suites pass.🤖 Generated with Claude Code