Repository navigation
Description
Before submitting
- I searched existing issues and did not find a duplicate.
- I included enough detail to reproduce or investigate the problem.
Area
apps/desktop
Steps to reproduce
- Windows desktop app with the WSL backend enabled and running (
desktop.backendInstance.start {"id":"wsl:Ubuntu"}in desktop.trace.ndjson). - In settings, disable the WSL backend. The trace logs
desktop.settings.writeSettings,desktop.wslBackend.reconcilewith eventtearing down WSL backend,desktop.ipc.wsl.setEnabled. - Inside WSL:
pgrep -fa wsl-runtime. The~/.t3/wsl-runtime/sha256-…/t3 --bootstrap-fd 0process is still running and still listening. - Kill that process inside WSL.
Expected behavior
Step 2 stops the WSL runtime process and unregisters the instance from the backend pool. Nothing respawns it while the setting is off.
Actual behavior
The process survives the teardown (checked 90 seconds later). When it is killed, the pool supervisor restarts it:
desktop.backendInstance.scheduleRestartFiber "backend exited unexpectedly; restart scheduled"
desktop.backendInstance.start {"id":"wsl:Ubuntu"}
This happened twice in a row (00:19:12 and 00:20:25). The setting itself persisted (desktop-settings.json no longer contains wslBackendEnabled), and a full app restart does not start the WSL backend, so only the live reconcile path is affected.
Side effect: the respawned WSL runtime shares ~/.t3/userdata with a t3 service install server in the same distro, and each one re-registers its own port with the relay on startup (last writer wins), which repeatedly broke T3 Connect for the Linux environment.
Impact
Degrades the experience (needs a full app restart to actually disable WSL; breaks remote access when combined with a WSL service).
Version or commit
Desktop 0.0.42 (Windows).
Environment
Windows 11, WSL2 Ubuntu (networkingMode=mirrored), t3 service install inside the same distro.
Logs or stack traces
00:17:14 desktop.settings.writeSettings
00:17:14 desktop.wslBackend.reconcile ["tearing down WSL backend"]
00:17:14 desktop.ipc.wsl.setEnabled
00:19:12 desktop.backendInstance.scheduleRestartFiber ["backend exited unexpectedly; restart scheduled"]
00:19:12 desktop.backendInstance.start {"id": "wsl:Ubuntu"}
00:20:25 desktop.backendInstance.scheduleRestartFiber ["backend exited unexpectedly; restart scheduled"]
00:20:26 desktop.backendInstance.start {"id": "wsl:Ubuntu"}
Workaround
Restart the desktop app after disabling the WSL backend.
Activity
juliusmarminge commentedon Sep 19, 2026
Triage
Confirmed as a real desktop bug in the live WSL disable path. Settings persist correctly; a full app restart correctly stays off. Only the running-app reconcile/unregister path fails, and the pool supervisor can then respawn
wsl:<distro>.What the traces show
The reported sequence matches the dual-mode disable path:
desktop.ipc.wsl.setEnabled(false)persists viaapplyWslWindowsFallback(wslBackendEnabledomitted fromdesktop-settings.json).desktop.wslBackend.reconcilelogstearing down WSL backendand callspool.unregister("wsl:Ubuntu").~/.t3/wsl-runtime/…/t3 --bootstrap-fd 0process is still running afterward.desktop.backendInstance.scheduleRestartFiber(backend exited unexpectedly; restart scheduled) followed bydesktop.backendInstance.start { id: "wsl:Ubuntu" }.scheduleRestartonly fires whendesiredRunningis stilltrue. So teardown logged a stop but left the instance supervisor running.wsl-only disable and “disable local environment” already relaunch the app. Dual-mode WSL off is the path that tries a live
unregister.Likely cause
DesktopBackendPool.registeris supposed to fork a child scope per instance sounregistercan close it and runDesktopBackendManager.stop()(setsdesiredRunning = false, interrupts the restart fiber, SIGTERMs the child).In
apps/desktop/src/backend/DesktopBackendPool.ts, register capturesEffect.context<BackendInstanceFactoryRequirements>(). At runtime that is the entire fiber context, including the pool layerScope. It then does:Inner
Effect.provide(factoryContext)overwrites the outerScope.provide(instanceScope). Instance finalizers and restart fibers attach to the pool layer scope.unregistercloses an empty forked scope, drops the id from the registry, and returns immediately (same-second IPC completion). The supervisor stays alive; killing the Linux process looks like a crash and gets restarted.The file comment already says the child scope should own finalizers; the provide order plus full-context capture defeats that. There is no test that
register+start+unregisteractually stops the process or prevents a restart.Secondary risk after that fix: the child is
wsl.exe, and SIGTERM on the Windows relay may not kill the Linuxt3process. Worth verifying; if it survives, add an in-distro kill. The respawn itself is explained by the supervisor never stopping.Connect /
t3 serviceside effectThe WSL backend and
t3 service installin the same distro both use~/.t3/userdata. Each register their port with the relay on startup (last writer wins). A ghost supervisor that keeps restarting the desktop WSL runtime will keep stealing Connect from the service. Related class of problem: #9652.Suggested fix
instanceScopeactually ownmakeBackendInstance(pick a context that does not includeScope, and/or provideinstanceScopeso it wins).instance.stop()beforeunregisterso teardown does not depend only on scope close.desiredRunningis false, and an exit does notscheduleRestart/start.t3process dies; if not, kill it inside the distro.Workaround remains: restart the desktop app after disabling the WSL backend.
Related
~/.t3/wsl-runtime