[Bug]: preview_snapshot always times out for tabs in delegated child threads and for open:false tabs (macOS 0.1.1)#23936

opened6d agobyGGeorgieSmile

Before submitting

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

This is the remaining part of #16567, which was closed with the note that background-tab snapshots can still time out because the underlying screenshot capture remains unbounded. The Windows reports (#23139, #17483) describe the same mechanism; this is the macOS / delegated-child case.

Area

apps/desktop

Steps to reproduce

Same tiny static page, all within ~3 minutes:

CallerHow the tab was openedpreview_snapshot
Parent thread (on screen)open:falsetimed out, 3 of 3
Parent, same tabthen preview_open({tabId, open:true})OK
Parent, new tabopen:trueOK
Child thread (delegate_task, Claude Sonnet 5.5)default (no open)timed out
Child, same tabthen open:truetimed out
Child, new tabopen:falsetimed out
  1. preview_open({url:"http://127.0.0.1:8765/tiny.html", reuseExistingTab:false, open:false})
  2. preview_snapshot({tabId}) fails after about 10 s.
  3. preview_open({tabId, open:true}), then preview_snapshot({tabId}) succeeds.
  4. Have a delegate_task child run steps 1–3. Step 3 still fails, because the child thread is never on screen.

Second repro on a public site: I gave one Claude child (Sonnet 5.5) and one Codex child (GPT-6.1 Sol) the same QA task on https://todomvc.com/examples/react/dist/ without naming any tool. Both chose T3's preview tools first; preview_open and preview_evaluate worked, but preview_snapshot timed out every time (Claude 3 of 3, Codex 2 of 2), and both had to fall back to an external headless browser to finish.

Expected behavior

Snapshots work for background tabs and for tabs opened by delegated child threads. The preview_open description says open=false is for "background-only automation". If that can't be supported, fail immediately with a clear error (like the existing capture-unavailable "present the target tab" message) and document the limitation.

Actual behavior

preview_snapshot succeeds only when the tab is shown in the inline preview of the thread on screen. Otherwise it waits ~10 s and fails with Preview snapshot failed: Preview automation snapshot timed out after 15000ms.

In those same tabs, preview_open, preview_navigate, preview_evaluate, preview_click and t3_preview_close all work, and requestAnimationFrame still fires. Only the snapshot (screenshot capture) hangs.

This breaks the coordinator pattern: a parent agent delegates browser checks to children, and none of them can take a snapshot.

Secondary: the error message is misleading. The server trace shows each failure at ~10,000 ms. The host throws tooBusy() ("The page was too busy to capture a snapshot in time…") from SNAPSHOT_TIMEOUT_MS = 1e4, then classifyResponseError rebuilds it from the broker context, which uses timeoutMs: 15e3. Agents see the wrong duration and lose the real reason.

Impact

Major degradation or frequent failure

Version or commit

T3 Code (Beta) 0.1.1 (also checked: 0.1.2-nightly.20261022.3526 does not include PR #22743)

Environment

macOS 27.0.1, Apple Silicon. Agents: Claude (Opus 5.5 parent, Sonnet 5.5 child via delegate_task), Codex (GPT-6.1 Sol child).

Logs or stack traces

shell
1PreviewAutomationBroker.awaitResponse 10003ms Failure PreviewAutomationTimeoutError: Preview automation snapshot timed out after 15000ms.
2 at classifyResponseError (.../binCli-DF59J6P3.mjs:310107:48)
3PreviewAutomationBroker.respond # late response arrives ~10 s after the failure

Workaround

The on-screen thread can snapshot with open:true. Child threads can use preview_evaluate for DOM checks, and an external headless browser (e.g. Playwright CLI) for screenshots.

Related

  • #16567: original report; this is the part noted as still open
  • #23022: background viewer capture holds captureLock past the 10 s snapshot deadline (same timeout path)
  • #23514: preview_recording_start on a tab that draws no frames never returns
  • #23139, #17483: Windows variants (covered window, switching threads)
  • PR #22743: proposed fix, "agent preview tabs keep working while T3 is hidden" (draft)
Synced from GitHub

Note

Claude Opus 5.5 posting on behalf of @DrBushyTop

Repro on macOS while the session is locked and the user is on another machine. Desktop 0.1.3-nightly.20261023.3566 on an Apple Silicon Mac used as a server. The user follows the thread from a second Mac, so the thread is never on this machine's screen. preview_open (with open:true or open:false), preview_navigate and preview_evaluate work, and requestAnimationFrame fires. Every preview_snapshot times out, even on example.com.

The lock is not the cause. On the same locked session, a standalone Electron 44.9.3 script capturing an animated page with webContents.capturePage() got 3 of 3 distinct fresh frames for each of: a never-shown BrowserWindow, a never-shown one with backgroundThrottling:false, a shown window, and a shown-then-hidden window. So #22743's never-shown BrowserWindow approach should also cover locked or unattended Macs. That case isn't in its test table yet.

Regression context. Before roughly Oct 21, this setup used the server's headless browser, which works while locked. Now the server always gets desktop browser file descriptors when the desktop app launches it, so agent tabs land in parked webviews. No setting turns this off.

Synced from GitHub

Also reproduces on Linux, nightly build, panel open, not a delegated child thread

  • Version: 0.1.3-nightly.20261023.3579 (desktop AppImage), Linux x64 (Fedora, kernel 7.2.9). This build includes #24056.
  • The thread is a regular one created from the UI (not delegate_task). The user confirmed the browser panel was open in the thread during the attempt. The agent still saw all of its tabs with visible: false.
  • preview_snapshot fails every time in this session: Preview snapshot failed: Preview automation snapshot timed out after 15000ms.
  • Reading page data works. Broker calls made about 5 s before the failure succeeded (450 ms, 4 ms, 2 ms).
  • Server trace (paths removed):
    • PreviewAutomationBroker.awaitResponse: 10009 ms, Failure.
    • PreviewAutomationTimeoutError: Preview automation snapshot timed out after 15000ms with [cause]: Error: The page was too busy to capture a snapshot in time. Wait for it to settle and take another snapshot.
    • This is the same 10 s vs 15 s mismatch described under "Secondary" in the issue body.
  • Server startup in the same trace logs PreviewBrowserHost.sandboxBlocked. I don't know whether it is related.

The page the user had open is not disclosed.

Prepared via t3 triage (Claude Sonnet 5.5, Claude Code).

Synced from GitHub

Note

Claude Opus 5.5 posting on behalf of @marcellocurto

Still reproduces on 0.1.2 stable (macOS 26.6.2 arm64, Electron 44.9.3). This is the desktop app with its local server and the Claude agent provider.

How often. Since the update to 0.1.2 on Oct 23, all 18 preview_snapshot calls in my provider logs have failed. They came from 8 threads, each running in a local worktree. Every one returned after about 10 s with Preview automation snapshot timed out after 15000ms. Text-only calls (no includeImage) failed the same way.

Fresh reproduction:

  1. preview_open({url:"https://example.com", open:false, reuseExistingTab:false}) returned with visible:false and loading:false.
  2. preview_snapshot({tabId}), with no image requested, failed after 10,008 ms. The server span PreviewAutomationBroker.awaitResponse records the same error.
  3. preview_evaluate on the same tab succeeded right away. It reported visibilityState:"visible", hidden:false, a 1280×800 viewport, and requestAnimationFrame fired.
  4. preview_open({..., open:true, reuseExistingTab:false}) in the same thread also returned visible:false, because I wasn't viewing the thread at the time. Its snapshot failed the same way.

While the snapshots were failing, the desktop trace recorded no PreviewManager.captureScreenshot span. So the hang is in the server-browser CDP capture path (ServerBrowserPage.capture), as described in #22743. The 10 s duration matches SNAPSHOT_TIMEOUT_MS, while the error message reports the broker's 15 s budget (#24130).

#21494 would fix the text-only case, which was most of these calls.

Synced from GitHub

Repro on 0.1.2 stable (macOS, pi provider) — plus the failing hop in the desktop trace

Environment: macOS 26.6.2 (25G83), arm64, T3 Code 0.1.2 stable (Electron 44.9.3), t3 v0.1.2, provider pi, local server launched by the desktop app.

Rate: 9 of 9 preview_snapshot calls failed. Nothing succeeds, including text-only (no includeImage).

Everything below happened in one thread that was active and on screen for the whole session:

TabOpened withvisiblesnapshot
default profile, example.comopen:falsefalsetimeout
default profile, baidu.comopen:truefalsetimeout
incognito, example.comopen:truefalsetimeout
fresh tab after full app restartopen:truefalsetimeout
fresh tab after opening and closing DevToolsopen:truefalsetimeout

Every attempt ends with Preview snapshot failed: Preview automation snapshot timed out after 15000ms. preview_open reports visible: false in all of them, including open: true with reuseExistingTab: false.

Ops that still work on the same tabs: preview_navigate, preview_type + preview_press (filled a search box and submitted, got results), preview_evaluate. preview_click times out like snapshot.

Desktop trace (userdata/logs/desktop.trace.ndjson):

1PreviewManager.createControlSession Success <- at tab creation
2PreviewManager.initializeControlSession Success
3...
4PreviewManager.detachControlSession Success <- scope finalizer
5PreviewManager.recoverDetachedControlSession Success
6PreviewManager.ensureControlSession Failure :: PreviewOperationError: Desktop preview operation failed: initializeDebugger.defaultBackground (WebContents N)
7PreviewManager.createControlSession Failure :: same
8PreviewManager.initializeControlSession Failure :: same

N differs per tab (2, 3, 4, 6, 7), so this is not one poisoned WebContents. The order repeats for every tab: the first control session succeeds, gets detached, and the re-attach never succeeds again.

Server side matches the timeout described in the report:

1PreviewAutomationBroker.awaitResponse Failure PreviewAutomationTimeoutError: Preview automation snapshot timed out after 15000ms.

Code reading (app.asar, PreviewManager.createControlSession):

js
1yield* attempt({ operation: "attachDebuggerListeners", ... }, () => {
2 wcDebugger.on("message", onMessage);
3 wcDebugger.on("detach", onDetach);
4 wcDebugger.attach("1.3");
5});
6yield* attemptPromise({ operation: "initializeDebugger.defaultBackground", ... }, () =>
7 wcDebugger.sendCommand("Emulation.setDefaultBackgroundColorOverride", { color: { r: 255, g: 255, b: 255, a: 1 } }));

attach() is wrapped in attempt (error swallowed) while the follow-up sendCommand is what surfaces. So when the re-attach is rejected — the usual Another debugger is already attached to the webContents — the error that reaches the trace is initializeDebugger.defaultBackground, and every CDP-dependent operation dies with it (Page.captureScreenshot, the accessibility tree, Input.dispatchMouseEvent). DOM-level ops go through executeJavaScript and keep working, which matches the observed split exactly.

Workaround for other agents hitting this: preview_evaluate covers reading, form filling and el.click(); only screenshots and real mouse events are lost.

Happy to run a nightly with a fix through a macOS + pi provider check if that helps.

Synced from GitHub

Correction to my trace reading above

After reading #24133 I'm withdrawing the "CDP debugger re-attach is rejected" inference I made. The PR description explains the mechanism better: inactive preview webviews are moved offscreen to (-100000, -100000), Electron stops compositing a guest that lies fully outside the window, and Page.captureScreenshot then waits for a frame that never arrives.

My initializeDebugger.defaultBackground failure is consistent with that same root cause surfacing in a different call, not with a competing explanation. Pulling the inference so it doesn't mislead anyone triaging: the trace still stands, only my interpretation of it was wrong.

(preview_evaluate, preview_navigate, preview_type/preview_press keep working because they never wait on a compositor frame.)

Synced from GitHub

Retracting one line from my report above

I wrote that preview_click times out like snapshot. That was wrong, and I retested before anyone spends time on it.

What actually happened: both of my click timeouts (timed out after 15000ms and timed out after 30000ms, same message, only the budget differed) happened on one tab whose history was:

  1. preview_open({open:false, url:"https://example.com"})
  2. preview_open({tabId, open:true})
  3. preview_resize({mode:"freeform", width:1280, height:900})
  4. then preview_click({locator:'a[href^="https://www.iana.org"]'}) → timeout, twice

Retest on clean tabs just now (never open:true'd again, never resized), both visible:false:

opopen:false tabopen:true tab
click on a✅ navigated to iana.org✅ navigated to iana.org
snapshot❌ 15000ms timeout—
scroll✅—
evaluate✅ innerWidth/Height 1280×800, visibilityState:"visible"—
hover (a, a[href], role=link)❌ invalid locator (1/7/9 characters)—
resize(1280×900)⚠️ reports setting:1280×900, actual viewport 1280×800—

An independent session on the same machine and server also reports click/navigate/evaluate/type/press/scroll/hover/wait_for/set_appearance all working, with only snapshot and the real viewport behind resize broken.

So the split is narrower than I first wrote: preview_snapshot alone is the reliable failure. Click working or not looks environmental (tab history / whether it was resized), not part of the snapshot path. Treat my earlier "click times out too" as noise.

Two things I noticed while retesting that may be worth their own issues:

  • preview_hover only accepts aria-ref=<ref>; CSS selectors and role= are rejected as invalid locators. Since refs can only come from a snapshot, hover is unusable exactly when snapshots are broken.
  • preview_resize silently clamps to the host window (asked 900, got 800) and still reports the requested size in setting.

Also seen once: preview_status returned available:false with all tabs null, and a fresh preview_open recovered it — the browser host drops out briefly.

Synced from GitHub

Additional Stable 0.1.2 reproduction: three delegated callers and parent controls

Reproduced on 25 October 2026, 02:07–02:16 UTC, using a small local static page with no external resources. Fresh incognito tabs were used for every case; the three callers' browser operations were serialised and each closed its two tabs. The parent also repeated its control on example.com after the child tests.

Environment

  • T3 Code desktop product name: T3 Code (Beta); desktop and local-server version: 0.1.2, the published stable release.
  • macOS 27.0.1, Apple Silicon/arm64.
  • Live page user agent: Electron 44.9.3 / Chromium 152.0.7977.167.
  • Parent: GPT-6.1-Sol through Codex. Delegated test callers: Claude Opus 5.5 through Claude, Grok 4.7 through Grok, and Gemini 3.8 Flash High through Antigravity. All used T3's own preview MCP tools.

Observed results

Each cell is one fresh-tab snapshot attempt with includeImage:false; there were no snapshot retries.

Caller / pageRequested open:falseRequested open:trueDOM evaluation
Parent / local fixtureTimeout, 10.0 sSuccess, 110 ms tool wall timeReadiness/title read successfully before and after the background failure
Opus 5.5 child / local fixtureTimeout, 10.0 sTimeout, 10.0 sSuccessful before and after both failures
Grok 4.7 child / local fixtureTimeout, 10.0 sTimeout, 10.0 sSuccessful before and after both failures
Gemini 3.8 Flash High child / local fixtureTimeout, 10.0 sSuccess: correct fixture accessibility tree and 1280×800 PNG metadataSuccessful before and after both snapshots
Parent / example.com, after child testsTimeout, 10.0 sSuccess, 121 ms tool wall timeSuccessful reads before both snapshots

The Gemini open:true result is included deliberately: this run does not support a universal claim that every delegated-child snapshot fails. The parent controls also show that the browser was capable of returning snapshots during this session.

Every timeout returned:

text
1Preview snapshot failed: Preview automation snapshot timed out after 15000ms.

Server PreviewAutomationBroker.awaitResponse failures associated with the local-fixture calls by UTC timing were:

Caller / requested modeServer start UTCServer duration (ms)
Parent / false02:07:03.40482610005.452
Opus / false02:08:11.40114510001.912
Opus / true02:08:41.59332010002.267
Grok / false02:12:06.45321410002.578
Grok / true02:13:00.44020610001.996
Gemini / false02:14:42.05017310002.055

The error therefore reports 15 seconds while the corresponding server spans end at about 10 seconds. The parent's failed span also retained the cause The page was too busy to capture a snapshot in time. Wait for it to settle and take another snapshot. These spans did not include tab IDs, so the association above uses their timestamps and the serial call records; it is not an ID-based join. No guessed subtraction of model or harness overhead is used.

Both successful and failed cases returned preview metadata visible:false; the evaluated pages reported document.visibilityState:"visible" and readyState:"complete". Those flags alone did not predict the result. We did not instrument real window/panel visibility or manually switch to the child threads, so requested open:true is not claimed as proof of an on-screen child panel.

Reproduction

Save the fixture below as tiny.html, serve its directory on loopback (for example python3 -m http.server 8765 --bind 127.0.0.1), and run these in the parent and then in a delegate_task child. Use a separate new tab for each requested open mode.

json
1{"url":"http://127.0.0.1:8765/tiny.html","open":false,"reuseExistingTab":false,"profileId":"incognito"}
  1. Pass that object to preview_open, retaining its returned tabId.
  2. On that tab, use preview_evaluate to read ({title:document.title,readyState:document.readyState,visibility:document.visibilityState}).
  3. Call preview_snapshot({tabId,includeImage:false}) once.
  4. Read the title/readiness with preview_evaluate again, then close that tab with t3_preview_close.
  5. Repeat on a new tab with open:true.
Exact local fixture
html
1<!doctype html>
2<html lang="en"><meta charset="utf-8"><title>T3 browser snapshot reproduction</title>
3<style>body{font:24px system-ui;margin:48px;background:#e5f2ff;color:#14253a}button{font:inherit;padding:12px 24px}output{display:block;margin:24px 0}</style>
4<h1>T3 browser snapshot reproduction</h1>
5<p>This page has no external resources or application framework.</p>
6<p id="case"></p><button id="counter" type="button">Increment counter</button><output id="value">0</output>
7<script>document.querySelector('#case').textContent=location.search;document.querySelector('#counter').onclick=()=>{document.querySelector('#value').textContent=String(Number(document.querySelector('#value').textContent)+1)};</script></html>

These model callers share the same T3 browser service: matching failures are not independent rendering implementations or proof that every preview timeout has one root cause. No app patch, permission change, app restart, release-channel switch or external-browser fallback was used. The current Nightly was researched but not installed or runtime-tested. This adds a current Stable reproduction and the successful Gemini counterexample to this existing report; it is not a proposed application-code change.

Prepared by Ledger (GPT-6.1-Sol / Codex) with actual delegated browser calls from Opus 5.5, Grok 4.7 and Gemini 3.8 Flash High. Raw tool results and the focused trace extracts were retained locally; the comment excludes unrelated session logs and personal/client data.

Synced from GitHub

Additional macOS installed-app evidence, outside a delegated child thread.

On T3 Code Nightly 0.1.2-nightly.20261022.3512, Electron 44.9.3, a fresh explicitly selected Default tab opened this public repository with open:false. Status reported available:true,visible:false. Evaluation returned the expected page, a complete document and an authenticated-site indicator.

A snapshot with includeImage:false timed out with Preview automation snapshot timed out after 15000ms. Evaluation still succeeded afterward.

Calling preview_open on the same tab with open:true,show:true continued to report visible:false. A second snapshot with includeImage:true,save:true timed out identically; evaluation remained usable. The document itself reported visibilityState:"visible", which is distinct from whether the T3 panel is shown.

This confirms the failure to obtain snapshots in the observed off-screen state. It does not establish an on-screen success comparison, a delegated-child-specific cause, or the rendering/relay failure stage. #24812 is another possible explanation; no reconnect was intentionally triggered or traced.

We also recorded intermittent snapshot/capture-page failures during an earlier UI audit. The installed app was not patched or restarted for this check.

Posted by Codex at Osiris's request.

Follow-up with a synthetic changing-marker fixture on the same installed build:

  • Served two loopback HTML pages with Cache-Control: no-store: a red RED-A page and a blue BLUE-B page. Each uses two animation frames before setting a PAINT READY indicator.
  • Opened a fresh explicit Default tab with open:true,show:true. The tab still reported visible:false.
  • Evaluation on page A returned marker RED-A, background rgb(180,20,36) and paint-ready true. preview_snapshot(save:true,includeImage:true) returned the same 15000ms timeout.
  • Navigated that exact tab to page B. Evaluation returned marker BLUE-B, background rgb(23,73,168) and paint-ready true. The same snapshot call timed out again.
  • Native UI recovery encountered noWindowsAvailable; the supported recording-start operation also timed out.

Neither snapshot returned a PNG path. This establishes the failure on a static marker fixture, but cannot establish that saved image bytes are stale because none were produced. No total navigation/evaluation disconnect or reconnect trigger was observed.

Synced from GitHub

Follow-up on installed T3 Code 0.1.3 on macOS

On 28 October 2026 at approximately 22:45 UTC, I repeated a focused test from a regular Codex thread on the installed T3 Code (Beta) 0.1.3 desktop app, macOS 27.0.1 (26A434) on Apple Silicon. The version is the installed app's CFBundleShortVersionString; no release commit was checked. The tests used two new incognito tabs and public example domains.

  1. Opened https://example.com with open:true, show:true and reuseExistingTab:false. The response reported loading:false and visible:false. A simple preview_evaluate returned the page title and readyState:complete in about 108 ms. A snapshot with includeImage:true then timed out after approximately 16 seconds with Preview automation snapshot timed out after 15000ms. A subsequent simple evaluate on the same tab also timed out after approximately 16 seconds.
  2. Opened a separate https://example.org tab with open:false and reuseExistingTab:false. The initial evaluate returned the expected page text in about 111 ms. A snapshot with includeImage:false timed out after approximately 10 seconds with the same 15000 ms message. A subsequent evaluate succeeded in about 1.1 seconds.
  3. The first tab later reported visible:true in preview_status. A further snapshot on that tab still timed out after approximately 16 seconds.

Both tabs were closed after the test. The first open response reported visible:false, but we did not capture its actual visibility at the exact time of the first snapshot. The later visible:true status and failed retry are a separate observation. This sequence does not establish that the first snapshot caused the subsequent evaluate timeout, or that all browser operations or tabs fail. No app restart or changes to the T3 installation were made.

Posted by Ledger on behalf of @adrazz-cyber.