Repository navigation
Description
Filed by Claude (Fable 5.1, running inside T3 Code) on behalf of Gal Hadad (@Gal-WPsite), at his request. Gal reviewed the summary; the investigation and reproduction below were done by the agent on his Mac.
Before submitting
- I searched existing issues and did not find a duplicate. [Bug]: Preview automation host disappears mid-session ("No preview automation host is available for <op> in environment …") and never reconnects #12146 and [Bug]: Optional 500 ms preview metadata timeout disconnects the automation host #12273 cover the host being evicted after a timeout; this report is about a deterministic cause of the timeout:
preview_resizecan never succeed while the main window is zoomed. - I included enough detail to reproduce or investigate the problem.
Area
apps/web (preview automation host in the renderer), with a side effect in apps/server
Steps to reproduce
- On macOS, in T3 Code (Alpha) 0.0.42, use View > Zoom In a few times so the main window runs at a zoom level other than 0 (Gal runs at level 2.5, i.e.
1.2 ** 2.5 = 1.577;window.devicePixelRatioin the renderer reads1.577on a non-Retina 1920x2160 display). - From an agent, call
preview_openwith any URL (e.g.https://example.com). - Call
preview_resizewith{ mode: "freeform", width: 1440, height: 900 }(any freeform or preset size reproduces).
Expected behavior
The resize completes and the guest reports a 1440x900 CSS viewport, independent of the main window zoom.
Actual behavior
preview_resize blocks for the full timeout and fails with Preview automation resize timed out after 15000ms. Because PreviewAutomationBroker.awaitResponse disconnects the host on timeout, every later preview/device tool then fails with No preview automation host is available for <op> in environment … until the window is reloaded (View > Reload) or closed and reopened (that part is #12146).
Root cause, read from the bundled renderer in app.asar (function names are the minified ones):
- The renderer's resize handler polls until
XU({ setting, appliedSettingKey, declaredViewport, renderedViewport })is true, whererenderedViewportcomes fromwebview.executeJavaScript("({ width: window.innerWidth, height: window.innerHeight })")and must match the requested CSS size within 1 px. - The desktop
PreviewManagerasserts the guest zoom factor totab.zoomFactor(defaultbrowserDefaultZoomFactor = 1) onregisterWebviewand again inreapplyZoomwheneverDesktopWindow.zoomMainchanges the main window zoom. - With the embedder at zoom 1.577 and the guest at zoom 1, a
<webview>styledwidth: 1280px(host CSS px) is 2019 device px, and the guest reportsinnerWidth = 2019. Measured on the affected machine:data-preview-css-width = 1280, guestinnerWidth = 2019,innerHeight = 1262. The comparison can never pass, so every resize times out. - Side effect even without automation: a "1280 px viewport" really lays out the page at 2019 CSS px, so responsive breakpoints shown in the preview are wrong whenever the UI is zoomed.
Verification of the diagnosis: setting the guest zoom to the host zoom from the renderer (webview.setZoomFactor(window.devicePixelRatio) on this dpr-1 display) makes the guest report 1280x800, and the same preview_resize to 1440x900 then succeeds immediately; a preset resize (iphone-12-pro) and mode: "fill" also succeed. Resetting the guest zoom to 1 brings the timeout back.
Impact
Any user who zooms the T3 Code UI (accessibility, high-resolution portrait monitors, non-Latin scripts) loses all preview automation on the first resize, and the failure mode is a 15 s hang followed by a permanent "no host" state, with no hint that the UI zoom is the cause.
Version or commit
T3 Code (Alpha) 0.0.42 (CFBundleVersion 0.0.42), app.asar dated 2026-09-16.
Environment
macOS (Darwin 25.5.0), Apple Silicon, single 1920x2160 display at scale 1. Agent provider: Codex (when first hit) and Claude Code (reproduction). Local environment, no T3 Connect.
Logs or stack traces
server.trace.ndjson, 2026-09-17 (Asia/Jerusalem), same environment id throughout:
23:50:01 PreviewAutomationBroker.connect ok
23:50:36 McpServer tools/call preview_resize {tabId:"tab_3",mode:"freeform",width:1440,height:900}
23:50:36 PreviewAutomationBroker.awaitResponse 15002ms PreviewAutomationTimeoutError: Preview automation resize timed out after 15000ms.
23:50:51 PreviewAutomationBroker.disconnect ok
23:50:51 ws.rpc.previewAutomation.connect (50718ms) Interrupted
23:50:51 PreviewAutomationBroker.invoke status PreviewAutomationNoAvailableHostError
23:50:52 PreviewAutomationBroker.invoke resize PreviewAutomationNoAvailableHostError
23:51:12 PreviewAutomationBroker.invoke open PreviewAutomationNoAvailableHostError
No new PreviewAutomationBroker.connect appears afterwards until the window is reloaded. desktop.trace.ndjson shows no resize-related PreviewManager span in that window; the request never left the renderer's wait loop.
Screenshots, recordings, or supporting files
Renderer state captured over the local DevTools port while the resize was pending:
{"inner":[532,1349],"dpr":1.5774409770965576,"outer":[840,2129],
"webview":{"key":"freeform:1280:800:","cssW":"1280","cssH":"800","zoomFactor":1,
"guestInner":{"width":2019,"height":1262}}}Workaround
- View > Actual Size (Cmd+0) before using preview automation; then View > Reload if the host is already gone.
- Gal's local RTL helper now keeps preview webviews' zoom factor equal to the main window zoom (snapped to Electron's half-level steps), which makes
preview_resizepass and restores correct breakpoints. A proper fix would either compare the rendered viewport in host CSS pixels (divide guestinnerWidthby the embedder zoom factor), or set the guest zoom tobrowserDefaultZoomFactor * mainWindowZoomFactorinassertTabZoom/reapplyZoom.
Activity
juliusmarminge commentedon Sep 17, 2026
Triage
Confirmed on current
main(4749035bd) and on v0.0.42. This is a real desktop preview-automation bug, not a flake and not already fixed. The report’s diagnosis matches the code, and the measured numbers are exact: zoom level 2.5 is1.2 ** 2.5 = 1.577440977, and1280 × 1.577440977 ≈ 2019,800 × 1.577440977 ≈ 1262.What happens
preview_resizeapplies the server snapshot, then the desktop renderer polls until the guest CSS viewport matches the request within 1 px:The
<webview>is styled to the requested size in host CSS pixels (data-preview-css-width/style.widthinHostedBrowserWebview.tsx). Preview-tab zoom is already compensated: layout multiplies the frame bytab.zoomFactor, andassertTabZoomsets the guest to that same factor, so a 1280 CSS request still reportsinnerWidth = 1280when the window zoom is 1.Window zoom is the opposite. Chromium inherits the embedder zoom onto the guest; desktop then resets it to
tab.zoomFactor(defaultbrowserDefaultZoomFactor = 1) so the preview “keeps its own zoom”:Same reset on
registerWebview(Manager.ts2190–2196) and on everyreapplyZoom(2617–2619). Result with window zoom 1.577 and guest zoom 1: a frame declared 1280×800 occupies ~2019×1262 device pixels, the guest reports that asinnerWidth/innerHeight, and the ±1 check can never pass. Existing tests lock this in:previewViewportReadiness.test.tsonly allows one pixel of slack;DesktopWindow.test.tsrequiresreapplyZoomafter everyzoomMain;Manager.test.tsexpectssetZoomFactor(tab.zoomFactor)with no window-zoom term.This is not Retina/
devicePixelRatioas a display scale. On a scale-1 display, rendererdevicePixelRatioequals the window zoom factor (the reporter’s1.5774409770965576). On Retina,devicePixelRatioincludes display scale; a default-zoom Retina Mac would already be broken if the guest used full DPR, and it is not. UsemainWindow.webContents.getZoomFactor(), notwindow.devicePixelRatio.Why the host then dies
waitForRenderedViewportburns the full request budget (default 15 s) and never replies. The broker treats an unanswered deadline as host death:That is the logged
Preview automation resize timed out after 15000ms.(PreviewAutomationTimeoutErrorinpackages/contracts/src/previewAutomation.ts), thenPreviewAutomationNoAvailableHostErroron every laterpreview_*until the window is reloaded. The host lives only in the Electron renderer (PreviewAutomationHostsreturnsnullwhen!isElectron), so a zoomed desktop window breaks automation even if the agent is driven from the website.Even without automation, the shown “1280 px” page actually lays out at ~2019 CSS px, so device-toolbar breakpoints are wrong whenever the UI is zoomed.
Not a duplicate
statusevicts the host. Same disconnect path, different trigger.Keep all three open. Fixing the zoom math stops this hang; it does not restore a host after some other timeout, and it does not make the optional metadata read non-evicting.
Suggested fix
Do not only relax
isPreviewViewportReady(divide guestinnerWidthby window zoom). That would make the wait succeed and leave breakpoints wrong.Compensate window zoom the same way preview-tab zoom is already compensated: at assert time, set
guestZoom = tab.zoomFactor * mainWindow.webContents.getZoomFactor()in
assertTabZoom/registerWebview.restoreZoomFactor/reapplyZoom. Keep storedtab.zoomFactoras the preview-only setting (defaultbrowserDefaultZoomFactor). After that, a 1280×800 declaration stays 1280×800 guest CSS at any UI zoom,preview_resize(freeform, preset,fill) becomes ready immediately, and the visible frame still tracks View > Zoom In.Do not use renderer
devicePixelRatioas the guest zoom (wrong on Retina). Snap only if Electron requires it;getZoomFactor()already matches the 0.5-level ladder (1.2 ** level).Tests to add/adjust:
assertTabZoom/reapplyZoomapply the product, nottab.zoomFactoralone, when the main window zoom is ≠ 1.DesktopWindow“restores preview zoom after zooming the app” test can stay; it only assertsreapplyZoomis invoked.Workaround
View > Actual Size (Cmd+0) before preview automation. If the host is already gone, View > Reload (or close/reopen the window). Same as the reporter.
Labels / next
bug,accepted,via-triage.guestZoom = tab.zoomFactor * mainWindowZoomFactorinassertTabZoom/ register /reapplyZoom. Leave [Bug]: Preview automation host disappears mid-session ("No preview automation host is available for <op> in environment …") and never reconnects #12146 (reconnect) and [Bug]: Optional 500 ms preview metadata timeout disconnects the automation host #12273 (non-evicting metadata) as separate follow-ups.ViaxCo commentedon Oct 6, 2026
Note
🤖 Claude Opus 5.5 responding on behalf of Victor
Still happens on nightly
0.0.46-nightly.20261005.2702(macOS, main window zoom 1.5). The saved size is 1280×800, the measured size is 1683×1052, andpreview_resizetimes out. #10536 fixes it in my test on currentmain: #10536 (comment)juliusmarminge commentedon Oct 7, 2026
Obsolete since #15328:
preview_resizeno longer polls the page's width, so window zoom can no longer time it out or evict the host. The underlying size mismatch under window zoom is tracked in #10525.