Skip to content

[Bug]: preview_recording_start on a tab that draws no frames never returns and blocks every later action on that tab #23514

Open

Description

@DavidMendesOS

Before submitting

  • I searched existing issues and did not find a duplicate.
  • I included enough detail to reproduce or investigate the problem.

Area

apps/server

Steps to reproduce

Follow-up to #16567, covering only the part left open there ("the underlying screenshot capture remains unbounded").

  1. Desktop app, an agent thread with preview tools.
  2. preview_open a page with open: false, so the desktop tab is not drawn.
  3. preview_snapshot: fails after ~10 s (the fix(server): keep sensitive values out of settings reads, startup logs and preview snapshots #20446 bound works). The tab is still usable.
  4. preview_recording_start on the same tab: the broker gives up at 15 s and evicts the host.
  5. preview_evaluate, preview_click on the same tab: each times out at 15 s and evicts the host again.
  6. t3_preview_close and a new tab is the only way out.

Server trace from that sequence on current main, run with vp run dev:desktop (UTC; durations from PreviewToolkit.invoke spans):

09:20:04  open             298 ms  ok
09:20:07  snapshot      10,010 ms  PreviewAutomationTimeoutError
09:20:20  recordingStart 16,002 ms timed out; broker disconnect/reconnect
09:20:39  evaluate      16,003 ms  timed out; disconnect/reconnect
09:20:58  click         16,003 ms  timed out; disconnect/reconnect
09:21:18  close              6 ms  ok

The same pattern happened on the released 0.1.1 with a tab opened with the default open: true (every tab there reported visible: false): snapshots failed at 10 s, then recordingStart, type, evaluate and click each failed at 16 s with an eviction until the tab was closed. It is intermittent there: earlier the same day, snapshots and a recording worked on a visible: false tab.

Expected behavior

A screenshot capture that cannot finish fails within a bound, the way preview_snapshot has since #20446, and the tab stays usable for the next action.

Actual behavior

captureViewport (apps/server/src/preview/ServerBrowserPage.ts) sends a bare Page.captureScreenshot with no deadline. preview_snapshot survives it only because snapshot() races the whole capture against SNAPSHOT_TIMEOUT_MS. The other callers have no such race:

  • startRecording (ServerBrowser.ts) awaits it for the first recording frame, inside the tab's SessionControl queue and its capture lock. When it never settles, the queue never advances, so every later agent action on the tab waits behind it until the tab closes.
  • The viewer still frame (pushStill) awaits it inside the same capture lock, so one stuck still blocks later snapshots and recordings too.

Side note that made this harder to read: a server-side PreviewAutomationTimeoutError from the 10 s snapshot bound reaches the agent as "Preview automation snapshot timed out after 15000ms", because the broker formats it with its own timeoutMs.

Impact

Major degradation or frequent failure

Version or commit

T3 Code (Beta) 0.1.1 desktop (tag at fe27e8e, includes #20446); reproduced on main @ 5ffe045 with vp run dev:desktop.

Environment

macOS 26.6.2, desktop app with its local server, server-side preview automation driving the desktop tab over CDP.

Workaround

Close the tab and open a new one. Avoid preview_recording_start on a tab whose snapshots are failing.

Notes on a fix

#22877 makes hidden desktop tabs draw while the server captures them, which should remove the usual trigger. Separately, a PR with a bound on captureViewport follows: any capture that still can't finish fails instead of holding the tab's queue and capture lock, for all three callers.

Filed by Claude Code (claude-opus-5-5) for @DavidMendesOS.

Activity

  1. DavidMendesOS commented on Oct 22, 2026

    @DavidMendesOS
    Author

    Related: #23022 (filed a day earlier) reports the same underlying fault from the viewer side: an unbounded captureViewport in the remote viewer's still frame holds tab.captureLock, so an agent snapshot waits before its own deadline starts. This issue is the preview_recording_start caller of the same function, which wedges the tab's agent queue instead of the capture lock.

    #23515 bounds captureViewport itself, so it covers both callers. For #23022 that only partly helps: a stuck still frame now releases the lock within 5 s, but a snapshot can still wait that long before its own 10 s bound begins.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions