Skip to content

[Bug]: Desktop window never appears on Linux/Wayland (Electron ready-to-show deadlock) #2216

Description

@mwolson

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

  1. Linux + Wayland session (reproduced on niri; root cause is in Electron, not the compositor).
  2. Run a recent nightly AppImage (e.g. T3-Code-0.0.20-x86_64.AppImage).
  3. Process starts and the renderer loads (verified via network requests to /api/project-favicon), but no window ever appears.

Expected behavior

The main window should appear once the renderer is ready, as on macOS / Windows / X11.

Actual behavior

No window appears. The main process is alive, the renderer process is alive, the React app loads, and the WebSocket connects, but the window stays hidden indefinitely.

Root cause

createWindow() in apps/desktop/src/main.ts constructs the BrowserWindow with show: false and reveals it on ready-to-show. On Wayland, ready-to-show only fires after show() is called for windows created with show: false, because the wl_surface has no role assigned until then and the compositor never reports the surface as ready. The standard "wait for ready, then show" pattern deadlocks: nothing ever calls show(), so ready-to-show never fires.

Confirmed by attaching the Node inspector to the running main process:

  • webContents.isLoading() transitioned to false and did-finish-load fired.
  • ready-to-show did NOT fire during the observation window.
  • After forcing window.show() from the inspector, ready-to-show then fired and the window appeared.

Impact

Blocks work completely

Version or commit

0.0.20 nightly. Reproduces on the current main (66c326b8).

Environment

Linux (niri compositor, Wayland session), Electron 40.6.0, AppImage build (T3-Code-0.0.20-x86_64.AppImage).

Workaround

None at runtime. Forcing show() from a debugger surfaces the window, but there is no user-facing workaround.

Fix

PR incoming: also subscribe to did-finish-load (Linux only) and reveal on whichever event fires first.

Activity

  1. mwolson commented on Apr 20, 2026

    @mwolson
    ContributorAuthor

    Some upstream context worth having on this ticket:

    • Canonical Electron tracking issue: electron/electron#48859 ("Event ready-to-show not triggering/inconsistent trigger on wayland"). Still open, regression since 37.9.0, reproducing on 38.x / 39.x / 40.x. Has a minimal repro gist.
    • Root cause per alexsch01's investigation: Chromium's DidMeaningfulLayout never fires in the affected paths, which is what gates ready-to-show. Filed upstream as Chromium issue 479458083.
    • Prior-art workaround: FreeTubeApp/FreeTube#8294 (merged 2025-11-18) added a did-finish-load fallback. An Electron maintainer pointed users to that PR as the sanctioned workaround while a proper upstream fix is pending.

    As mentioned on #2217, the fix there uses the same did-finish-load fallback with two minor tweaks: the gate is process.platform === "linux" rather than ozone-platform=wayland (broader, catches Electron 38+'s auto-selected Wayland backend), and both listeners stay bound with first-wins semantics so the no-flash timing is preserved on X11 where ready-to-show still works.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions