Skip to content

fix(preview): preview tool errors name the step that failed - #855

Merged
incognitojam merged 1 commit into
mainfrom
fix/preview-automation-failure-reasons
Oct 15, 2026
Merged

incognitojam merged 1 commit into
mainfrom
fix/preview-automation-failure-reasons

Conversation

@incognitojam

Copy link
Copy Markdown
Owner

When a preview tool failed in the desktop app, the agent's error rarely said why. The desktop app's own waits (for the browser tab to start, the page to load, or the tab to render) are 5 seconds or less, but all of them reached the agent as "timed out after 15000ms", the request's overall deadline. Desktop failures such as a stalled page capture became a generic "failed on client …". With no cause to go on, agents concluded that the Browser tab failed because it was not visible, and repeated that across threads. Preview tools are designed to work on hidden tabs.

Change

  • A failure can now carry a fixed failure stage from the client to the server, next to the existing allowlisted failure reason. The stages are tab starting, navigation, tab rendering, attaching to the tab, and page capture. Only these names cross the boundary; the server drops any other value, so no page content reaches the server or error reporting.
  • The desktop preview manager adds a stage token to its IPC error message for page capture, control session, and navigation failures. A page-capture timeout also reports the TimeoutError reason.
  • The web host maps its own timeouts to a stage, and the server writes the stage into the agent's message, for example "Preview automation snapshot failed while capturing the page image (TimeoutError) on client …" or "Preview automation snapshot timed out while waiting for the browser tab to render."
  • When the desktop app never answers, the error now says "timed out after 15000ms waiting for the desktop app to respond."
  • The preview_status description no longer lists "visibility" without context: it says the field reports whether the tab is shown to the user, and that preview tools work on tabs the user cannot see.
  • The fork-server-error-reporting ledger entry records the stage alongside the existing reason codes.

Validation

  • Desktop: the existing stalled-capture test now checks that the IPC error text the renderer receives yields the page-capture stage and the TimeoutError reason.
  • Web: a viewport render timeout serializes with the viewport-render stage, and a desktop IPC capture failure serializes with page-capture and TimeoutError.
  • Server: the broker turns a remote timeout with a stage into "timed out while waiting for the browser tab to render", turns an execution failure with a stage into "failed while capturing the page image (TimeoutError)", and drops an unknown stage.
  • Shared: the parser reads a stage only from Electron IPC errors, with or without a reason suffix, and ignores unknown stages.
  • vp test run on the four affected files (178 tests) plus the MCP toolkit, tool presentation, and MCP HTTP server tests (30 tests). Typecheck passes for contracts, shared, server, web, and desktop.

Not verified: I did not cause a real capture failure in a running desktop app. A stall is hard to trigger on purpose, so the full path from desktop app to agent is covered only by these per-boundary tests.

A related upstream fix for snapshots that time out or return stale images when the desktop window is minimized or covered, pingdotgg#13600, is tracked in #854.


Written by an agent (Claude Code, claude-opus-5-5).

Client-side preview waits (webview registration, navigation, viewport
rendering) all reached agents as "timed out after 15000ms", the request
deadline rather than the wait that failed, and desktop failures such as a
stalled page capture became a generic "failed on client". Agents filled
the gap by blaming the Browser tab not being visible.

Add an allowlisted failure stage that crosses the client boundary next to
the existing failure reason. The desktop app tags capture, control
session, and navigation failures in its IPC message; the web host maps
its own timeouts; the server names the step in the agent's error. A
broker timeout with no client answer now says the desktop app did not
respond. Capture timeouts report the TimeoutError reason, and the
preview_status description says tools work on tabs the user cannot see.
@github-actions

Copy link
Copy Markdown

Upstream divergence

Both sides are compared with upstream at the in-order tip 06e627448b. Excludes .repos/ and pnpm-lock.yaml.

Measure Base This PR Change
Upstream files the fork changes 1,383 1,384 +1
Lines changed in them 65,202 65,349 +147
Upstream files the fork removes 41 41 0
Fork-only files 457 457 0
Fork-only lines 81,169 81,231 +62

Upstream files that start to differ (1)

  • apps/server/src/mcp/toolkits/preview/tools.ts: +1 −1 from upstream
Upstream files whose difference changes (7)
File Change Now differs by
apps/server/src/mcp/PreviewAutomationBroker.test.ts +42 166 lines
packages/contracts/src/previewAutomation.ts +34 88 lines
apps/web/src/components/preview/previewAutomationErrors.ts +23 26 lines
apps/desktop/src/preview/Manager.ts +17 595 lines
apps/web/src/components/preview/previewAutomationRequestConsumer.test.ts +16 105 lines
apps/desktop/src/preview/Manager.test.ts +10 1,075 lines
apps/server/src/mcp/PreviewAutomationBroker.ts +3 20 lines

@github-actions

Copy link
Copy Markdown

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

ℹ️ The exact PR base did not have a successful artifact. Baseline uses the latest successful main measurement shown below.

Provider Metric Main baseline This PR Impact PR ceiling
Codex Total thread wire 5.0 KiB 4.9 KiB −40 B (−0.8%) 6.8 KiB ✅
Codex Thread snapshot wire 3.8 KiB 3.8 KiB 0 B (0.0%) 4.9 KiB ✅
Codex Live turn WebSocket wire 1.2 KiB 1.2 KiB −40 B (−3.3%) 2.0 KiB ✅
Codex Live turn WebSocket decoded 20.9 KiB 20.8 KiB −41 B (−0.2%) 29.3 KiB ✅
Codex Live turn messages 2 1 −1 (−50.0%) 8 ✅
Claude Total thread wire 5.0 KiB 5.0 KiB 0 B (0.0%) 6.8 KiB ✅
Claude Thread snapshot wire 3.8 KiB 3.8 KiB 0 B (0.0%) 4.9 KiB ✅
Claude Live turn WebSocket wire 1.2 KiB 1.2 KiB 0 B (0.0%) 2.0 KiB ✅
Claude Live turn WebSocket decoded 21.2 KiB 21.2 KiB 0 B (0.0%) 29.3 KiB ✅
Claude Live turn messages 2 2 0 (0.0%) 8 ✅

Baseline: 305d9c5 · PR result: 58c95ac · Source CI: success

Scenario and decoded snapshot size

10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.

  • Codex decoded thread snapshot: 108.5 KiB
  • Claude decoded thread snapshot: 108.8 KiB

Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed.

@incognitojam
incognitojam merged commit 26608d8 into main Oct 15, 2026
28 checks passed
@incognitojam
incognitojam deleted the fix/preview-automation-failure-reasons branch October 15, 2026 16:02
incognitojam pushed a commit that referenced this pull request Nov 7, 2026
)

Co-authored-by: Claude Opus 5.5 <[email protected]>

Fork adaptation: Keep fork step-specific and no-response timeout messages from fork #855, and append upstream's bounded click-target reason to that summary so both diagnostics survive.

(cherry picked from commit 10f3657)
Upstream-PR: 23489
Fork-Feature: fork-server-error-reporting
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant