[Bug]: Preview tabs from other threads stay alive as hidden, unthrottled webviews until restart (13 tabs, ~2.7 GB) #15579
Description
Activity
Note
Grok responding on behalf of Julius.
Triage
Thanks so much for the careful measurements, @davidvornholt, they made this easy to trace. I checked it on current
main(eac52f0087) and on v0.0.45 (6c8fed35dd), and the relevant code is the same on both: preview tabs you aren't looking at stay mounted as live Chromium guests for the rest of the session.What I found
ElectronBrowserHostsits outside the router and mounts aHostedBrowserWebviewfor every preview session in every thread, not just the thread on screen (apps/web/src/browser/ElectronBrowserHost.tsx). A thread stays in that index as long as it has sessions (syncActivePreviewThreadinapps/web/src/previewStateStore.ts).closeTabruns only when the React guest unmounts (apps/web/src/browser/desktopTabLifetime.ts), which happens only after an explicitpreview.closefrom the UI or frompreview_close/t3_preview_close. Closing the panel or switching threads doesn't close tabs.- On Linux, an inactive guest is parked at
(-100000, -100000)withvisibility: hidden(apps/web/src/browser/hostedBrowserWebviewStyle.ts).data-preview-rendering="suspended"is only a marker for automation. Nothing calls Chromium freeze or discard, and nothing destroys the guest. - The webview preferences are only
contextIsolation=false,sandbox=true,nodeIntegration=false(apps/desktop/src/preview/WebviewPreferences.ts). Electron's default background throttling only applies once Chromium considers the page backgrounded, and the guest is temporarily set tosetBackgroundThrottling(false)while recording or in picture-in-picture. - The repo never calls
navigator.getGamepads(), so the Gamepad polling wakeups very likely come from a script inside one of those guests, which fits your theory. ResourceCleanupServiceonly cleans up terminals and attachments, and thread deletion, archive, and settlement don't touch previews (apps/server/src/orchestration-v2/ThreadDeletion.ts).
How this differs from the related issues you linked:
- [Bug]: Inactive macOS previews still render with panel closed: 228% GPU/guest CPU and ~10 GB graphics footprint #14730 is the macOS case, where inactive guests stay visible and keep painting. Linux already takes the hidden path, and hiding wouldn't stop these renderers anyway.
- [Bug]: Deleting a thread leaves its preview sessions in PreviewManager for the life of the server #14966 covers sessions left behind after a thread is deleted. Here the threads are still alive.
- [Bug]: After a preview_resize timeout evicts the automation host, the orphaned hidden preview tab makes the GPU process leak memory without bound (21 GB on a 16 GB Mac, machine unusable) #13610 is an orphaned hidden tab after a
preview_resizetimeout. These tabs were never evicted.
Likely fix area
A few options a maintainer could weigh:
- Really suspending inactive guests (panel closed, other thread, and not recording, in picture-in-picture, or held by automation). That could mean freezing them, which keeps page state, or discarding the webview and recreating it from the session URL when the thread is shown again, which frees the memory but reloads the page.
- Keeping recording, picture-in-picture, and automation on the existing
renderingActivepath. - Separately, closing previews when a thread is idle or settled, the way idle terminals already close. That would change current behavior, since tabs are kept today so they're still there when you come back.
Restarting the app or closing each thread's previews remains the workaround for now.
A maintainer will decide on the fix direction.
- addedbugSomething is broken or behaving incorrectly.Something is broken or behaving incorrectly.via-triageFiled through npx t3 triageFiled through npx t3 triage
on Oct 4, 2026 Note
Grok responding on behalf of Julius.
Reopening: #16961 (merge
d9f7c0c) still listedFixesfor this issue, but the hidden-tabs commit was reverted before merge (f3bbe7f— Revert "fix(web): hidden browser tabs on macOS stop painting").mainstill keeps inactive macOS guests paintable viakeepPaintableWhenInactive: isMacPlatform(...), so this is not fixed yet.Additional measured incident on macOS with the newer server-hosted browser path. Posting here to avoid duplicating the broader retained-preview report; the headless rendering path may warrant separate tracking. Related: #17090.
Environment:
- T3 Code Nightly
0.0.46-nightly.20261009.2861, installed commit3b6af0bd1600. - macOS 26.6, build 25G72, Apple Silicon (
Mac17,9), 15 CPU cores, 48 GiB RAM. - Managed
chrome-headless-shell154.0.8037.92.
The user reported mild lag across the system. During read-only inspection:
- A T3-owned headless browser's graphics process consumed 140.9% CPU in a timed
topsample and 153.7% in a separate rollingpsreading (100% = one core). - Browser launch flags included
--disable-gpu --enable-unsafe-swiftshader --force-device-scale-factor=2 --disable-background-timer-throttling --disable-renderer-backgrounding. - Its graphics process used
--use-angle=swiftshader-webgl. - The browser had been running for approximately 4 hours 42 minutes and was parented by the T3 backend.
- T3's Electron GPU/interface processes and WindowServer were also busy; total system CPU still had about 60% idle capacity.
- Memory pressure was normal, swap was zero, data-volume free space was about 846 GiB, and
pmset -g thermreported no thermal/performance warning. preview_statusandt3_preview_listfor the diagnostic thread returned no tabs. These tools are thread-scoped, so this does not establish that tabs in other threads were inactive or had no automation consumers. Their individual visibility was not inspected.
At the user's request to close previews, SIGTERM was sent only to the identity-verified shared headless browser root process. Its graphics process and all managed headless-browser processes then disappeared; the T3 desktop and backend remained running. A subsequent timed system sample had 72.4% CPU idle (the preceding sample had 74.9%). This was a short live observation, not a controlled benchmark, and no claim is made that all perceived lag was resolved. The browser was stopped at process level because the available normal tab-close tool only covered the diagnostic thread; preview-session metadata cleanup was not verified.
This supports investigating the CPU cost and lifecycle of retained server-hosted browser tabs on local macOS installations, in addition to the earlier native webview reports. It does not establish a memory leak, an exact reproduction sequence, or that SwiftShader alone caused the system-wide lag. Inactive tabs without automation, recording, or viewing consumers should suspend rendering, and users should be able to discover and close retained tabs across threads.
Measurements and this comment prepared with Codex on the user's behalf.
- T3 Code Nightly
Before submitting
Area
apps/desktop
Steps to reproduce
preview_open(13 tabs across a few threads here).preview_closeeither.Expected behavior
Preview tabs that the user can't see, in threads they're not looking at, should be throttled, frozen or discarded. They should also be closed eventually, for example when the thread is idle, archived or deleted. They shouldn't keep a full renderer running for the rest of the session.
Actual behavior
Nothing ever closes them. The desktop trace log shows 13
createTaband 0closeTabsince start.ElectronBrowserHostmounts aHostedBrowserWebviewfor every session in every thread. Hidden ones are only moved offscreen with CSS, and there is nobackgroundThrottling, freeze or discard. The pages keep running scripts, including the ad and tracker iframes of the visited sites.PreviewManagerholds the sessions in memory until an explicitclose(), andResourceCleanupServicedoesn't close previews.Measured 2026-10, with the T3 Code window visible on another thread and no preview panel open:
navigator.getGamepads()(fingerprinting scripts do this).None of this is visible in the UI, so the user can't tell that the previews are still open.
Related: #14966 (sessions outlive a deleted thread), #14730 (inactive macOS previews keep rendering) and #13610 (orphaned hidden tab leaks GPU memory). This issue is the Linux, cross-thread version: threads that are neither deleted nor orphaned.
Impact
Major degradation or frequent failure
Version or commit
v0.0.45 (desktop)
Environment
NixOS, Linux 7.2.8, GNOME (Wayland), Intel Lunar Lake laptop on battery
Workaround
Restart T3 Code (preview sessions aren't persisted), or open each thread and close its previews.