What happened
I was signed in to an internal web app by hand, in the collaborative browser preview panel, and could see three tabs showing a logged-in dashboard. The agent I asked to read a page from that app told me I was not signed in. It said so three times, each time with what looked like solid evidence: the login form's markup, and document.cookie === "". Nothing errored, nothing timed out. I only worked out it was wrong by screenshotting my own screen and showing it.
Quitting and relaunching T3 Code fixed it.
Diagnosis
Two preview automation hosts were registered in the environment at the same time. Every automation request went to the one the user was not looking at, and it answered successfully from its own browsing context.
1. Two clients were connected with preview surfaces for the whole session. ws.rpc.previewAutomation.connect opened for connection 44c44045… at 08:45:37 and for 4842efea… at 08:55:10. Both stayed open until PreviewAutomationBroker.disconnect fired for both at 10:49:07, 64 ms apart, when the app quit. Both reported ws.rpc.preview.reportStatus throughout.
2. Every automation request in the fault window went to the client that was not being focused. Between 10:38:00 and 10:49:00 (local, UTC+1): PreviewAutomationBroker.invoke × 61, ws.rpc.previewAutomation.respond × 61 — all 61 responses from 4842efea…, none from 44c44045…. In that same window 44c44045… issued ws.rpc.previewAutomation.focusHost × 11, including 20 s and 4 s before the first wrong read. 4842efea… issued 2.
3. The desktop main process never executed any of them. The pre-restart desktop trace has 5,024 spans in that window, including desktop.ipc.preview.createTab × 6, registerWebview × 18, PreviewManager.createControlSession × 6 and closeTab × 2 — and zero automationEvaluate, automationStatus, automationClick or automationSnapshot. It logged those before the fault (last at 10:23:33, PreviewManager.performAutomationEvaluate) and again immediately after the restart (10:49:47). So tab creation went through the desktop — which is why the panel visibly followed preview_open — while the reads were answered somewhere else entirely.
4. The restart collapsed it to one host and the fault went away. PreviewAutomationBroker.make at 10:49:23, then a single connection d74d3b7c… at 10:49:26 issuing both focusHost and respond. Desktop automation spans returned, preview_open returned visible: true, and reads matched the panel.
Why the wrong host wins. In apps/server/src/mcp/PreviewAutomationBroker.ts, a provider session with no live lease picks its host by:
.sort((left, right) =>
right.supportedOperations.size - left.supportedOperations.size ||
Number(right.focused) - Number(left.focused) ||
right.focusOrder - left.focusOrder)[0]
and the winner is pinned in assignments, keyed by environmentId \0 providerSessionId, then deliberately never re-evaluated while the connection lives. apps/web/src/components/preview/PreviewAutomationHosts.tsx registers every host with supportedOperations: [...PREVIEW_AUTOMATION_OPERATIONS], so between two current clients the first sort key always ties and the decision falls through to focused / focusOrder — effectively registration order. Because the lease is keyed by providerSessionId, a new agent session re-runs that sort, which matches the timeline: the session that started at 10:38 was new, and reads at 10:20 and 10:23 in the same app run were correct.
Why preview_status cannot detect it. currentStatus in the same file returns available: Boolean(previewBridge?.automation) — true whenever the Electron bridge object exists, regardless of whether the responding client has that tab rendered — and when the tab is not desktop-backed it falls back to the server snapshot's navStatus for url and title rather than reading the live page. Hence available: true and a plausible URL throughout. No field in any preview_* response distinguishes this from a healthy read.
Two things that look like the cause and are not.
visible: false is not a signal. It was false for the correct reads at 10:20 and 10:23 in the same app run, false throughout the fault, and false on a healthy single-host session afterwards while reads were correct. Only the post-restart call returned true.
- The preload
TypeError in the evidence below fired on every page load in the fault window and looks causal. It is not specific to this fault: the same two lines appear in preview_snapshot consoleEntries in local provider logs from 2026-09-11, 09-16 and 09-17, in sessions whose reads were correct.
What I could not determine. Server traces rotate every 2–4 minutes on this machine, so the window before 10:23:50 was already gone and I cannot show which host served the correct earlier reads, nor the moment routing changed. Span attributes in both trace files are empty, so environmentId, clientId and tabId are not recoverable from them — the connection identities above are traceId/parentSpanId pairs for the WebSocket connections. I also could not identify what created the second host: the desktop trace shows no new renderer at 08:55:10, only a desktop.ipc.connectionCatalog.set moments earlier, and the on-disk catalog is encrypted.
Two minor asides, not filed separately. preview_status on a tab id that does not exist returns {"available":true,"visible":false,"tabId":null} rather than an error, which reads like a healthy answer. And tab ids in the fault window were tab_7, tab_8, tab_b, tab_c, restarting at tab_1 after the relaunch — they are not decimal and not enumerable, which cost time during triage.
Steps to reproduce
I could not reproduce this deterministically, and would rather say so than invent steps. Observed once; traces for most of the fault window are preserved and I can attach them.
Predicted from the traces and the v0.0.42 source, but not confirmed:
- Get two preview automation hosts registered in one environment at once (I have not identified what created the second one).
- Sign in to a site by hand in the preview panel you can see.
- Start a new agent session — a new
providerSessionId — and have it call preview_evaluate or preview_snapshot against that site.
Verified negative control: with a single registered host, the same sequence against a throwaway localhost server behaves correctly. preview_navigate to a cookie-setting endpoint followed by preview_evaluate returns the signed-in document and the real document.cookie.
Version
0.0.42 (tag v0.0.42, commit 719a76c)
Environment
macOS 27.0 (26A428) darwin arm64, Electron 44.1.0, Chrome 152.0.7977.65, desktop app against the local server, provider Claude Code (claudeAgent) with Opus 5, preview device toolbar active (Responsive, 1280x800)
Evidence
# two hosts connected, both alive until the app quit (server.trace.ndjson*)
08:45:37 ws.rpc.previewAutomation.connect trace 44c44045 parent f7386175
08:55:10 ws.rpc.previewAutomation.connect trace 4842efea parent 2dc7c84e
10:49:07 PreviewAutomationBroker.disconnect 4842efea
10:49:07 PreviewAutomationBroker.disconnect 44c44045
# fault window 10:38:00-10:49:00, routing counts
PreviewAutomationBroker.invoke 61
ws.rpc.previewAutomation.respond 61 ALL from 4842efea, 0 from 44c44045
ws.rpc.previewAutomation.focusHost 11 from 44c44045 (2 from 4842efea)
# same window, pre-restart desktop trace (5024 spans total)
desktop.ipc.preview.createTab 6
desktop.ipc.preview.registerWebview 18
PreviewManager.createControlSession 6
desktop.ipc.preview.automationEvaluate 0
desktop.ipc.preview.automationStatus 0
desktop.ipc.preview.automationClick 0
desktop.ipc.preview.automationSnapshot 0
# last desktop-executed automation before the fault, first after the restart
10:23:33.105 PreviewManager.performAutomationEvaluate Success 2.0ms
10:49:47 desktop.ipc.preview.automationEvaluate Success
# agent-visible results across the restart boundary (site redacted: private intranet)
10:38:30 preview_open {"available":true,"visible":false,"tabId":"tab_7", ...}
10:45:43 preview_open {"available":true,"visible":false,"tabId":"tab_b", ...}
10:47:06 preview_open {"available":true,"visible":false,"tabId":"tab_c", ...}
10:46:27 preview_evaluate {"cookie":"","txt":"Username Password Sign in with ...", ...}
10:47:13 preview_evaluate tabId=tab_c -> "CLAUDE-MARKER-TAB-C" (user saw no change on screen)
10:47:15 preview_evaluate tabId=tab_b -> "CLAUDE-MARKER-TAB-B" (user saw no change on screen)
--- app quit and relaunch at 10:49:07 ---
10:49:44 preview_open {"available":true,"visible":true,"tabId":"tab_1", ...}
# on every page load in the fault window, and also in unaffected sessions since 2026-09-11
Electron sandboxed_renderer.bundle.js script failed to run
TypeError: Cannot destructure property 'preloadScripts' of 'binding.startupData' as it is null.
at node:electron/js2c/sandbox_bundle:2:133687
at node:electron/js2c/sandbox_bundle:2:134791
at ___electron_webpack_init__ (node:electron/js2c/sandbox_bundle:2:134795)
at node:electron/js2c/sandbox_bundle:2:134918
Related issues
Not a duplicate of #11167 (wrong client, but calls time out; here it answers successfully), #12146, #12898, #12273, #12319 (host eviction/disconnect, all loud), #11223, #7212, #3715 or #8332.
Fix applied or workaround
Nothing was written to the machine. Workaround is to quit and relaunch T3 Code: the Electron profile survives, so a persistent login survives, but a non-persistent session cookie does not and you will need to sign in again.
Filed by
claude (opus-5) in T3 Code, following .github/triage/PLAYBOOK.md
What happened
I was signed in to an internal web app by hand, in the collaborative browser preview panel, and could see three tabs showing a logged-in dashboard. The agent I asked to read a page from that app told me I was not signed in. It said so three times, each time with what looked like solid evidence: the login form's markup, and
document.cookie === "". Nothing errored, nothing timed out. I only worked out it was wrong by screenshotting my own screen and showing it.Quitting and relaunching T3 Code fixed it.
Diagnosis
Two preview automation hosts were registered in the environment at the same time. Every automation request went to the one the user was not looking at, and it answered successfully from its own browsing context.
1. Two clients were connected with preview surfaces for the whole session.
ws.rpc.previewAutomation.connectopened for connection44c44045…at 08:45:37 and for4842efea…at 08:55:10. Both stayed open untilPreviewAutomationBroker.disconnectfired for both at 10:49:07, 64 ms apart, when the app quit. Both reportedws.rpc.preview.reportStatusthroughout.2. Every automation request in the fault window went to the client that was not being focused. Between 10:38:00 and 10:49:00 (local, UTC+1):
PreviewAutomationBroker.invoke× 61,ws.rpc.previewAutomation.respond× 61 — all 61 responses from4842efea…, none from44c44045…. In that same window44c44045…issuedws.rpc.previewAutomation.focusHost× 11, including 20 s and 4 s before the first wrong read.4842efea…issued 2.3. The desktop main process never executed any of them. The pre-restart desktop trace has 5,024 spans in that window, including
desktop.ipc.preview.createTab× 6,registerWebview× 18,PreviewManager.createControlSession× 6 andcloseTab× 2 — and zeroautomationEvaluate,automationStatus,automationClickorautomationSnapshot. It logged those before the fault (last at 10:23:33,PreviewManager.performAutomationEvaluate) and again immediately after the restart (10:49:47). So tab creation went through the desktop — which is why the panel visibly followedpreview_open— while the reads were answered somewhere else entirely.4. The restart collapsed it to one host and the fault went away.
PreviewAutomationBroker.makeat 10:49:23, then a single connectiond74d3b7c…at 10:49:26 issuing bothfocusHostandrespond. Desktop automation spans returned,preview_openreturnedvisible: true, and reads matched the panel.Why the wrong host wins. In
apps/server/src/mcp/PreviewAutomationBroker.ts, a provider session with no live lease picks its host by:and the winner is pinned in
assignments, keyed byenvironmentId \0 providerSessionId, then deliberately never re-evaluated while the connection lives.apps/web/src/components/preview/PreviewAutomationHosts.tsxregisters every host withsupportedOperations: [...PREVIEW_AUTOMATION_OPERATIONS], so between two current clients the first sort key always ties and the decision falls through tofocused/focusOrder— effectively registration order. Because the lease is keyed byproviderSessionId, a new agent session re-runs that sort, which matches the timeline: the session that started at 10:38 was new, and reads at 10:20 and 10:23 in the same app run were correct.Why
preview_statuscannot detect it.currentStatusin the same file returnsavailable: Boolean(previewBridge?.automation)— true whenever the Electron bridge object exists, regardless of whether the responding client has that tab rendered — and when the tab is not desktop-backed it falls back to the server snapshot'snavStatusforurlandtitlerather than reading the live page. Henceavailable: trueand a plausible URL throughout. No field in anypreview_*response distinguishes this from a healthy read.Two things that look like the cause and are not.
visible: falseis not a signal. It wasfalsefor the correct reads at 10:20 and 10:23 in the same app run,falsethroughout the fault, andfalseon a healthy single-host session afterwards while reads were correct. Only the post-restart call returnedtrue.TypeErrorin the evidence below fired on every page load in the fault window and looks causal. It is not specific to this fault: the same two lines appear inpreview_snapshotconsoleEntriesin local provider logs from 2026-09-11, 09-16 and 09-17, in sessions whose reads were correct.What I could not determine. Server traces rotate every 2–4 minutes on this machine, so the window before 10:23:50 was already gone and I cannot show which host served the correct earlier reads, nor the moment routing changed. Span attributes in both trace files are empty, so
environmentId,clientIdandtabIdare not recoverable from them — the connection identities above aretraceId/parentSpanIdpairs for the WebSocket connections. I also could not identify what created the second host: the desktop trace shows no new renderer at 08:55:10, only adesktop.ipc.connectionCatalog.setmoments earlier, and the on-disk catalog is encrypted.Two minor asides, not filed separately.
preview_statuson a tab id that does not exist returns{"available":true,"visible":false,"tabId":null}rather than an error, which reads like a healthy answer. And tab ids in the fault window weretab_7,tab_8,tab_b,tab_c, restarting attab_1after the relaunch — they are not decimal and not enumerable, which cost time during triage.Steps to reproduce
I could not reproduce this deterministically, and would rather say so than invent steps. Observed once; traces for most of the fault window are preserved and I can attach them.
Predicted from the traces and the v0.0.42 source, but not confirmed:
providerSessionId— and have it callpreview_evaluateorpreview_snapshotagainst that site.Verified negative control: with a single registered host, the same sequence against a throwaway localhost server behaves correctly.
preview_navigateto a cookie-setting endpoint followed bypreview_evaluatereturns the signed-in document and the realdocument.cookie.Version
0.0.42 (tag v0.0.42, commit 719a76c)
Environment
macOS 27.0 (26A428) darwin arm64, Electron 44.1.0, Chrome 152.0.7977.65, desktop app against the local server, provider Claude Code (claudeAgent) with Opus 5, preview device toolbar active (Responsive, 1280x800)
Evidence
Related issues
Not a duplicate of #11167 (wrong client, but calls time out; here it answers successfully), #12146, #12898, #12273, #12319 (host eviction/disconnect, all loud), #11223, #7212, #3715 or #8332.
Fix applied or workaround
Nothing was written to the machine. Workaround is to quit and relaunch T3 Code: the Electron profile survives, so a persistent login survives, but a non-persistent session cookie does not and you will need to sign in again.
Filed by
claude (opus-5) in T3 Code, following .github/triage/PLAYBOOK.md