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.

  2. mredwin1 commented on Oct 26, 2026

    @mredwin1

    A related case on released 0.1.2 (macOS, Retina) where preview_recording_start does succeed, but recording a busy page wedges the server:

    1. Fill-viewport tab on chatgpt.com (1280x800 CSS, deviceScaleFactor 2), preview_recording_start, then send a prompt that streams a long answer with an embedded app iframe.
    2. While it streams, preview_evaluate / preview_wait_for / preview_status all time out at 15 s.
    3. preview_recording_stop afterwards timed out at 120 s three times; the recording was never saved and preview_snapshot on the tab hung too.

    event-loop-stalls.ndjson at recording start: "utilization":1,"minorPageFaults":310094. Once, the desktop then respawned the backend and every attempt failed with ServerAlreadyRunningError (the old process still held the home lock), so MCP calls got ECONNREFUSED until restart.

    From the bundle, the likely cause: RECORDING_SCREENCAST is { format: "jpeg", quality: 90, everyNthFrame: 1 } with no maxWidth/maxHeight, so every frame is a 2560x1600 q90 JPEG shipped as base64 through the server into the encoder page; and stopRecording holds captureLock while awaiting Promise.all(framesInFlight), so a frame backlog blocks stop and every capture-locked call. Capping screencast size, dropping frames while the encoder is busy, and bounding the in-flight wait in stop would likely fix it.

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