Repository navigation
Description
Summary
On Windows, when the persisted window tab references a WSL sidecar server (wsl:<distro>), the desktop renderer fatally errors on every launch because it resolves the active server before that WSL sidecar finishes booting. The app is left unusable (blank/dead window) and relaunching always reproduces.
The existing readiness guard (availableStartupServer) only protects defaultServerUrl; the restored tab's server — which drives activeServer() via the route's serverKey — is never validated against sidecar readiness.
Environment
- OpenCode Desktop 1.18.3 (latest; bug still present)
- OS: Windows + WSL2 (distro:
archlinux) - Server managed by the desktop's WSL sidecar subsystem
Reproduce
- Add a WSL server (
wsl:<distro>) in the desktop app and open a session on it (so a tab withserver: "wsl:<distro>"is persisted toopencode.window.<id>.dat). - Quit the desktop app.
- Relaunch.
Actual: the renderer dies immediately with:
Error: Notification server not found: wsl:archlinux
at ensure (…/main-…js)
at selected (…/main-…js)
at Object.ready (…/main-…js)
…
The window is unusable and every relaunch reproduces. There is no way to recover from inside the app (you cannot reach the UI to switch servers).
Log timeline (single launch)
The WSL sidecar is healthy — it just boots ~4s after the renderer has already crashed:
18:47:35.693 wsl sidecar starting { id: 'wsl:archlinux', distro: 'archlinux' }
18:47:37.096 native sidecar ready { url: 'http://127.0.0.1:53493' }
18:47:37.552 FATAL renderer error: Notification server not found: wsl:archlinux
18:47:41.372 wsl sidecar ready { id: 'wsl:archlinux', url: 'http://127.0.0.1:64643' } // ~3.8s AFTER the crash
Root cause
packages/app/src/context/notification.tsx — ensure() does a hard throw when the active server is absent from the server list:
const ensure = (key: ServerConnection.Key) => {
const conn = global.servers.list().find((item) => ServerConnection.key(item) === key)
if (!conn) throw new Error(`Notification server not found: ${key}`) // ← fatal on launch
}
const selected = () => ensure(activeServer())
return { ready: () => selected().ready(), … }activeServer() returns the restored route's serverKey (wsl:<distro>):
const activeServer = createMemo(() => {
if (params.serverKey) return requireServerKey(params.serverKey) // ← restored tab → wsl:<distro>
if (search.draftId) { … }
return server.key
})At that moment wsl:<distro> is not yet in global.servers.list(), because packages/desktop/src/renderer/wsl/connections.ts only includes WSL servers whose runtime has reached ready:
export function readyWslConnections(state?: WslServersState) {
return (state?.servers ?? []).flatMap((item) => {
if (item.runtime.kind !== "ready") return [] // sidecar still "starting" → excluded
return [ { … variant: "wsl", distro: item.config.distro, http: { url: item.runtime.url, … } } ]
})
}The availableStartupServer guard only rewrites defaultServerUrl, so it does not help when the active server comes from the restored tab/route:
export function availableStartupServer(defaultServer, state) {
const key = defaultServer ?? "sidecar"
if (!key.startsWith("wsl:")) return key
if (state?.servers.some((item) => item.config.id === key && item.runtime.kind === "ready")) return key
return "sidecar"
}Because the throw happens inside a root SolidJS computation (Object.ready → runComputation), it is treated as a fatal renderer error (recorded via recordFatalRendererError), not a recoverable one.
Suggested fix
Either/both:
- Gate the renderer's initial render / "ready" on the active server actually being present — extend the readiness gating (cf. fix(desktop): include wslServers loading in ready state #37218) so the restored tab's
serverKeyis also covered, not onlydefaultServerUrl. E.g. resolve the active server throughavailableStartupServer(or skip/wait) when its target is a WSL sidecar that hasn't reachedready. - Make
ensure()non-fatal during init: fall back to a present server (e.g. the native sidecar) or defer the computation instead of throwing when the active server is transiently absent.
Workaround (for anyone hitting this now)
Quit the desktop app, then in %APPDATA%\ai.opencode.desktop\:
- In
opencode.settings, set"defaultServerUrl": "sidecar". - Delete
opencode.window.<id>.dat(clears the restored tab that points atwsl:<distro>).
Relaunch → app boots on the native sidecar, then switch to the WSL server once it reports ready. Sessions are server-side and are not lost.
(Note: leaving a wsl:<distro> tab as the last active tab before quitting will reproduce the crash again until the readiness gating covers the restored tab.)
Activity
github-actions commentedon Jul 17, 2026
This issue might be a duplicate of existing issues. Please check:
izumiChan16 commentedon Jul 17, 2026
Methods available for the time being:
Open the software -> encounter a crash -> press
Ctrl+Shift+I-> just press F5 in the developer tools to refresh itShana-AE commentedon Jul 17, 2026
Update — found the exact trigger (and a verified workaround).
The crash isn't driven by the persisted window tab (
opencode.window.<id>.dat) as I first wrote — it's the router's restored last URL fromlocalStorage.packages/desktop/src/renderer/index.tsx:That URL looks like
/server/<base64("wsl:archlinux")>/session/<id>— theserverKeyis base64-encoded in the path (easy to miss when grepping forwsl:). On launch,params.serverKeydecodes towsl:<distro>, which feedsactiveServer()innotification.tsx→ensure()throws before the WSL sidecar reachesready.Because
params.serverKeytakes priority overdefaultServer, the existingavailableStartupServerguard ondefaultServerUrlcan't help here.Verified on the affected machine:
Notification server not found: wsl:archlinux~3.8s before the sidecar reported ready (e.g. crash20:49:56.678vs sidecar ready20:50:00.734)./): clean launch with an emptyrenderer.log, sidecar becomes ready normally (20:57:16.709), no fatal error.Suggested fix (refined): validate the restored
serverKey(fromgetLastActiveUrl) against the available/ready servers before the router commits to it — i.e. applyavailableStartupServer-style logic to the restored route's server, or makeensure()non-fatal during init when the active server is a not-yet-ready sidecar.Workaround: quit the app → delete
%APPDATA%\ai.opencode.desktop\Local Storage\(optionally also setdefaultServerUrl: "sidecar"inopencode.settings). Relaunch, then switch to the WSL server once it's ready. Caveat: leaving a WSL session as the last active view before quitting repopulateslocalStorageand reproduces the crash.fermumen commentedon Jul 21, 2026
Regression in Desktop 1.18.4 (Windows 11 + WSL2,
Ubuntu-26.04). The fix from #37890 is included in the installed release, but startup still fatally crashes with the same error.Current exported debug bundle timeline:
So this is the same transient sidecar-readiness condition, not a failing WSL server. The stack is now through
ProjectAvatar/HomeSessionRowand identifiesensureasensureServerState:#37890only changedselected()to fall back when the active key is not inglobal.servers.list(). Howeverpackages/app/src/pages/layout/project-avatar-state.tsbypassesselected():and in
v1.18.4,ensure()still contains:Thus any restored/home session row for a configured WSL server can still crash the renderer before that sidecar is registered. The direct API needs the same non-throwing fallback/no-op behavior (or callers must guard the connection), rather than relying on the
selected()fallback alone.The previously proposed fallback in unmerged/closed #37190 appears to address this direct-call path. A regression test should exercise
useSessionTabAvatarState(or another directensureServerState(wslKey)consumer) while the WSL sidecar is configured but not yet ready.Hona commentedon Jul 21, 2026
Weird - I got someone to test and it was fixed.
Investigating
fermumen commentedon Jul 21, 2026
tbh it worked briefly after the update but then same issue.
I did get it to work again by doing some resetting:
wsl:Ubuntu-26.04entry fromopencode.settingsopencode.workspace.wsl-*.datand server/project state)This was a clean reset of Desktop state; the WSL OpenCode session database was retained. The configured WSL server entry itself was valid and generated by Desktop, not manually added.
But now there is still an intermittent (related?) syntomp: Desktop reports that sessions no longer exist and asks to close their tabs. That suggests stale persisted tab/session references can survive or be recreated independently of the notification race. =
Hona commentedon Jul 22, 2026
Yeah follow up commit done for this crash