Skip to content

[codex] structure desktop preview failures#3244

Merged
juliusmarminge merged 5 commits into
mainfrom
codex/desktop-preview-structured-errors
Jun 20, 2026
Merged

[codex] structure desktop preview failures#3244
juliusmarminge merged 5 commits into
mainfrom
codex/desktop-preview-structured-errors

Conversation

@juliusmarminge

@juliusmarminge juliusmarminge commented Jun 20, 2026

Copy link
Copy Markdown
Member

Summary

  • replace generic preview and Playwright failures with structured Schema error unions
  • preserve exact low-level and remote browser causes without deriving messages from cause text
  • retain selector kind and bounded lengths while excluding raw selectors, locators, and browser exception detail from direct fields and messages
  • preserve selected evaluation and selector reasons only in the action-timeline presentation mapper
  • attach tab, WebContents, artifact, and operation context and remove constructor-only error wrappers
  • add focused regressions for safe encoded diagnostics, exact causes, artifact safety, and control interruption

Validation

  • vp test run apps/desktop/src/preview/Manager.test.ts (13 tests)
  • vp check (0 errors; 20 pre-existing warnings)
  • vp run typecheck

Note

Medium Risk
Broad refactor of error paths across preview automation, CDP control, and artifact I/O; callers still type as the union but behavior and failure shapes change. Privacy-sensitive encoding rules need to stay correct for IPC/logging.

Overview
Desktop preview and Playwright injection stop funneling failures through a single wrapped PreviewManagerError / generic automationError helpers. Failures are now typed Schema.TaggedError variants united as PreviewManagerError and PlaywrightInjectedRuntimeError, with operation, tabId, webContentsId, and artifact paths on operational errors.

Automation and artifacts get dedicated tags (evaluation, invalid selector, target not found, control interrupted, path outside directory, image load, recording conflict, etc.). User-facing message stays generic; toTimelineMessage and similar helpers supply detail from cause without baking browser/selector text into structural fields. Diagnostics use kinds and lengths (e.g. detailKind, selectorLength) so encoded payloads avoid raw selectors, locators, and exception bodies where tests require it; full cause is still retained for debugging.

attempt / fail wrappers are removed in favor of failing with the specific error class. Tests assert _tag shapes, capture failures, artifact sandboxing, clipboard empty image, interruption, and encoding privacy.

Reviewed by Cursor Bugbot for commit d1301b2. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Replace generic PreviewManagerError with a discriminated union of structured error types

  • Replaces the single PreviewManagerError wrapper class in Manager.ts with a Schema.Union of specific tagged error classes (e.g. PreviewOperationError, PreviewAutomationEvaluationError, PreviewArtifactPathOutsideDirectoryError, etc.), each carrying structured diagnostic fields.
  • Adds toTimelineMessage helpers on relevant error classes so timeline messaging derives from structured error data rather than generic strings.
  • Refactors attempt/attemptPromise helpers to attach operation name, tabId, and webContentsId to every PreviewOperationError, replacing prior ad-hoc error construction throughout the manager.
  • Structures Playwright injected runtime failures in PlaywrightInjectedRuntime.ts with dedicated tagged error classes for each failure mode (marker not found, evaluation failure, validation failure, etc.).
  • Behavioral Change: all PreviewManager API callers now receive specific tagged errors instead of a generic wrapper; any code pattern-matching on the old PreviewManagerError class will need updating.

Macroscope summarized d1301b2.

Co-authored-by: codex <codex@users.noreply.github.com>
@coderabbitai

coderabbitai Bot commented Jun 20, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 8a300a87-756d-4314-91da-85c0ee3b8710

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/desktop-preview-structured-errors

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:XXL 1,000+ changed lines (additions + deletions). labels Jun 20, 2026

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Autofix Details

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Timeline omits nested failure messages
    • Restored cause-unwrapping logic in finalizeControlAction so that when a PreviewOperationError occurs, the timeline records the underlying error.cause.message instead of the generic wrapper message.

Create PR

Or push these changes by commenting:

@cursor push 012e32aa35
Preview (012e32aa35)
diff --git a/apps/desktop/src/preview/Manager.ts b/apps/desktop/src/preview/Manager.ts
--- a/apps/desktop/src/preview/Manager.ts
+++ b/apps/desktop/src/preview/Manager.ts
@@ -851,11 +851,13 @@
       } else {
         const error = Option.getOrNull(Cause.findErrorOption(exit.cause));
         const interrupted = isPreviewAutomationControlInterruptedError(error);
+        const underlying =
+          isPreviewOperationError(error) && error.cause instanceof Error ? error.cause : error;
         yield* replaceAction(tabId, {
           ...actionEvent,
           status: interrupted ? "interrupted" : "failed",
           completedAt,
-          error: error instanceof Error ? error.message : String(error),
+          error: underlying instanceof Error ? underlying.message : String(underlying),
         });
       }
       const tabs = yield* SynchronizedRef.get(tabsRef);
@@ -2520,6 +2522,7 @@
 export type PreviewManagerError = typeof PreviewManagerError.Type;
 
 export const isPreviewManagerError = Schema.is(PreviewManagerError);
+export const isPreviewOperationError = Schema.is(PreviewOperationError);
 export const isPreviewAutomationControlInterruptedError = Schema.is(
   PreviewAutomationControlInterruptedError,
 );

You can send follow-ups to the cloud agent here.

Comment thread apps/desktop/src/preview/Manager.ts Outdated
macroscopeapp[bot]
macroscopeapp Bot previously approved these changes Jun 20, 2026
@macroscopeapp

macroscopeapp Bot commented Jun 20, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved

This PR refactors error handling by replacing a generic error class with specific tagged error types, adding structured metadata to errors. Runtime behavior is unchanged - errors occur at the same points with more informative types. The test updates verify the new structure.

You can customize Macroscope's approvability policy. Learn more.

Co-authored-by: codex <codex@users.noreply.github.com>
@macroscopeapp
macroscopeapp Bot dismissed their stale review June 20, 2026 09:31

Dismissing prior approval to re-evaluate 6168586

macroscopeapp[bot]
macroscopeapp Bot previously approved these changes Jun 20, 2026
Co-authored-by: codex <codex@users.noreply.github.com>
@macroscopeapp
macroscopeapp Bot dismissed their stale review June 20, 2026 12:10

Dismissing prior approval to re-evaluate 2431a34

macroscopeapp[bot]
macroscopeapp Bot previously approved these changes Jun 20, 2026
Co-authored-by: codex <codex@users.noreply.github.com>
@macroscopeapp
macroscopeapp Bot dismissed their stale review June 20, 2026 16:31

Dismissing prior approval to re-evaluate 09f6a22

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.

Fix All in Cursor

Bugbot Autofix prepared fixes for both issues found in the latest run.

  • ✅ Fixed: Automation timeline omits cause details
    • Added toTimelineMessage() static methods to PreviewAutomationEvaluationError and PreviewAutomationInvalidSelectorError that extract cause details, and updated finalizeControlAction to use them instead of falling through to the generic error.message.
  • ✅ Fixed: detailKind mismatches detailLength
    • Changed detailKind checks from !== undefined to truthiness checks and detailLength from ?? to || so that null and empty-string descriptions correctly fall through to the text field, keeping kind and length aligned.

Create PR

Or push these changes by commenting:

@cursor push 24ffcaf9ae
Preview (24ffcaf9ae)
diff --git a/apps/desktop/src/preview/Manager.ts b/apps/desktop/src/preview/Manager.ts
--- a/apps/desktop/src/preview/Manager.ts
+++ b/apps/desktop/src/preview/Manager.ts
@@ -876,9 +876,13 @@
         const interrupted = isPreviewAutomationControlInterruptedError(error);
         const errorMessage = isPreviewOperationError(error)
           ? PreviewOperationError.toTimelineMessage(error)
-          : error instanceof Error
-            ? error.message
-            : String(error);
+          : isPreviewAutomationEvaluationError(error)
+            ? PreviewAutomationEvaluationError.toTimelineMessage(error)
+            : isPreviewAutomationInvalidSelectorError(error)
+              ? PreviewAutomationInvalidSelectorError.toTimelineMessage(error)
+              : error instanceof Error
+                ? error.message
+                : String(error);
         yield* replaceAction(tabId, {
           ...actionEvent,
           status: interrupted ? "interrupted" : "failed",
@@ -915,13 +919,8 @@
         return Effect.fail(
           new PreviewAutomationEvaluationError({
             tabId,
-            detailKind:
-              description !== undefined
-                ? "exception-description"
-                : text !== undefined
-                  ? "exception-text"
-                  : "unknown",
-            detailLength: description?.length ?? text?.length ?? 0,
+            detailKind: description ? "exception-description" : text ? "exception-text" : "unknown",
+            detailLength: description?.length || text?.length || 0,
             cause: response.exceptionDetails,
           }),
         );
@@ -2467,6 +2466,13 @@
     cause: Schema.Defect(),
   },
 ) {
+  static toTimelineMessage(error: PreviewAutomationEvaluationError): string {
+    const cause = error.cause as
+      | { text?: string; exception?: { description?: string } }
+      | undefined;
+    return cause?.exception?.description || cause?.text || error.message;
+  }
+
   override get message(): string {
     return `Preview JavaScript evaluation failed in tab ${this.tabId}`;
   }
@@ -2513,6 +2519,11 @@
     cause: Schema.Defect(),
   },
 ) {
+  static toTimelineMessage(error: PreviewAutomationInvalidSelectorError): string {
+    const cause = error.cause as { message?: string } | undefined;
+    return cause?.message || error.message;
+  }
+
   get detail(): {
     readonly selectorKind: PreviewAutomationSelectorKind;
     readonly selectorLength?: number;
@@ -2595,6 +2606,10 @@
 export const isPreviewAutomationControlInterruptedError = Schema.is(
   PreviewAutomationControlInterruptedError,
 );
+export const isPreviewAutomationEvaluationError = Schema.is(PreviewAutomationEvaluationError);
+export const isPreviewAutomationInvalidSelectorError = Schema.is(
+  PreviewAutomationInvalidSelectorError,
+);
 
 export class PreviewManager extends Context.Service<
   PreviewManager,

You can send follow-ups to the cloud agent here.

Reviewed by Cursor Bugbot for commit 09f6a22. Configure here.

Comment thread apps/desktop/src/preview/Manager.ts
Comment thread apps/desktop/src/preview/Manager.ts Outdated
Co-authored-by: codex <codex@users.noreply.github.com>
@juliusmarminge
juliusmarminge merged commit 90bd5ee into main Jun 20, 2026
16 checks passed
@juliusmarminge
juliusmarminge deleted the codex/desktop-preview-structured-errors branch June 20, 2026 18:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL 1,000+ changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant