Skip to content

Upload action error codes surface to the agent as bare machine strings #522

@lmorchard

Description

@lmorchard

Context

Follow-up from #494 (no need to block that PR). The new upload_file action is solid; this is a small polish item about how its errors are communicated to the LLM.

Observation

The upload error codes (upload_disabled, upload_path_not_allowed, upload_path_not_file, upload_target_not_file_input, upload_path_required) are thrown as bare tokens:

throw new BrowserActionException("upload_file", "upload_path_not_allowed");

BrowserActionException passes the second arg straight through as error.message (packages/core/src/errors.ts:73), and performActionWithValidation returns it verbatim to the model with isRecoverable: true (packages/core/src/tools/webActionTools.ts:278). So the agent receives the literal string "upload_path_not_allowed" and a "you may retry" signal — with no semantics telling it the rejection is a path-policy decision that a different element can't fix.

This is inconsistent with the other action errors, which give the model actionable prose, e.g.:

// packages/core/src/errors.ts:44 (InvalidRefException)
`Invalid element reference '${ref}'. The element does not exist on the current page.
 Please check the page snapshot for valid element references.`

Observed effect (minor)

When a path is rejected, the agent occasionally retries upload_file against a different element ref before aborting — which can never succeed, since the rejection is about the path, not the element. It's intermittent and clusters in cases where the path looks valid (e.g. a symlink whose name/existence checks out but resolves outside the allowlist):

Scenario Runs Retried before abort
Plain outside-allowlist 4 0
Symlink escape 3 1

No correctness or security impact — the firewall blocks every attempt. Cost is at most a couple of wasted LLM round-trips.

Suggested fix

Replace the bare codes with self-describing messages that name the failure as a policy decision and steer away from retrying a different element, e.g.:

upload_path_not_allowed: '<path>' is outside the configured upload allowlist. A different element will not help — stop, or ask the user to adjust upload_allowed_paths.

Pure string change at the BrowserActionException / failedActionResult call sites (packages/core/src/browser/playwrightBrowser.ts:563,571,575,591,603,1203 and packages/core/src/tools/webActionTools.ts:377). Leave isRecoverable alone — that flag drives broader loop behavior and isn't worth the risk for this.

Note: the retry is LLM behavior, so the wording change is expected to reduce it, not guarantee elimination. Worth a quick before/after check if anyone wants to confirm the payoff.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions