Skip to content

fix(gui): pin Ozone to X11 on Linux so Electron 42 stops segfaulting on Wayland (#190) - #194

Merged
PAMulligan merged 2 commits into
mainfrom
fix/gui-wayland-x11-190
Nov 8, 2026
Merged

PAMulligan merged 2 commits into
mainfrom
fix/gui-wayland-x11-190

Conversation

@PAMulligan

Copy link
Copy Markdown
Collaborator

Closes #190. Part of #186.

What was actually wrong

Electron 42 resolves its Ozone (display) backend before the main script runs: with no flags on a Wayland session, app.commandLine.getSwitchValue('ozone-platform') already reads wayland inside main, and the process segfaults creating its first BrowserWindow (after ready). Measured on Ubuntu 26.04:

Launch Result
no flags SIGSEGV
app.commandLine.appendSwitch('ozone-platform', 'x11') in main (the fix the issue proposed) SIGSEGV
ELECTRON_OZONE_PLATFORM_HINT=x11 SIGSEGV
--ozone-platform-hint=x11 SIGSEGV (Electron still reports ozone-platform=wayland)
--ozone-platform=x11 on the command line window opens

So the pin has to come from whatever starts Electron.

The fix, two layers

  1. Launchers we own pass the switch up front (packages/gui/scripts/launch-flags.mjs, one shared rule):
    • dev, preview and start now go through scripts/run-electron-vite.mjs, which forwards --ozone-platform=x11 after electron-vite's -- separator on Linux, keeps a backend the caller chose there, and forwards termination signals to its child.
    • tests/smoke/smoke.mjs launches with it, so CI under Xvfb and a local Wayland session start the app the same way.
    • The AppImage's desktop entry carries it via appImage.executableArgs, together with the --no-sandbox default that setting executableArgs would otherwise drop (pinned by a packaging test).
    • Both launchers also strip ELECTRON_RUN_AS_NODE, which editor-hosted shells (VS Code) export and which turns the Electron binary into plain Node.
  2. Any other launcher is caught in the main process (src/main/x11-relaunch.ts): when Electron has already chosen Wayland on Linux and the caller passed no --ozone-platform… switch and no ELECTRON_OZONE_PLATFORM_HINT, the app relaunches itself once with the pin. Inside an AppImage it relaunches $APPIMAGE, because the mount disappears with the first process. This covers a bare electron out/main/index.js, dist/linux-unpacked/flavian and an AppImage started from a terminal (its AppRun only forwards the arguments it was given, so the desktop entry's pin does not apply there).

docs/GUI.md gains a Troubleshooting section covering both problems and the native-Wayland opt-out.

Verification (Ubuntu 26.04, Wayland, from a VS Code shell with ELECTRON_RUN_AS_NODE=1 set)

  • pnpm --filter @flavian/gui smoke: passes, no manual flags.
  • pnpm --filter @flavian/gui dev: window opens, still running after 30 s.
  • Bare electron out/main/index.js: relaunches once; the surviving process is electron --ozone-platform=x11 out/main/index.js.
  • dist/linux-unpacked/flavian: relaunches once; survivor flavian --ozone-platform=x11.
  • Flavian-2.1.2.AppImage from a terminal (extract-and-run mode, this host has no FUSE): relaunches through $APPIMAGE; survivor flavian --ozone-platform=x11. The same AppImage before this change died with exit 139.
  • Generated desktop entry: Exec=AppRun --no-sandbox --ozone-platform=x11 %U.
  • GUI lint, typecheck and 72 unit tests pass (15 new: the launch-flags rules, the relaunch decision, the packaging arguments).

🤖 Generated with Claude Code

Paul Mulligan and others added 2 commits November 8, 2026 14:29
…on Wayland (#190)

Electron 42 resolves its Ozone backend before the main script runs, picks
Wayland on a Wayland session (Ubuntu 26.04) and segfaults creating its first
window. Measured on that host: app.commandLine.appendSwitch('ozone-platform',
'x11'), ELECTRON_OZONE_PLATFORM_HINT=x11 and --ozone-platform-hint=x11 all
still crash; only --ozone-platform=x11 on the real command line works. So the
pin is applied by whatever starts Electron, in two layers:

- Launchers we own pass it up front (scripts/launch-flags.mjs): the new
  dev/preview runner scripts/run-electron-vite.mjs forwards it after
  electron-vite's `--` separator (and keeps a backend the caller chose
  there), the smoke test launches with it, and the AppImage's desktop entry
  carries it through appImage.executableArgs together with the --no-sandbox
  default that setting executableArgs would otherwise drop.
- For any other launcher (a bare `electron out/main/index.js`, the unpacked
  binary, an AppImage started from a terminal, whose AppRun only forwards
  its own arguments) the main process relaunches itself once with the pin
  (src/main/x11-relaunch.ts). It never relaunches when the caller passed any
  --ozone-platform… switch or ELECTRON_OZONE_PLATFORM_HINT, and inside an
  AppImage it relaunches $APPIMAGE, since the mount goes away with the first
  process.

The runner and the smoke test also strip ELECTRON_RUN_AS_NODE, which
editor-hosted shells export and which turns the Electron binary into plain
Node. docs/GUI.md gains a Troubleshooting section for both.

Verified on Ubuntu 26.04 Wayland: pnpm dev, pnpm smoke (from a VS Code
shell), a bare electron launch, dist/linux-unpacked/flavian and the AppImage
(extract-and-run mode, no FUSE here) all reach a live window; the two bare
launches relaunch once with the pin. gui lint, typecheck and 72 unit tests
pass.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@PAMulligan
PAMulligan merged commit 441d9af into main Nov 8, 2026
21 checks passed
@PAMulligan
PAMulligan deleted the fix/gui-wayland-x11-190 branch November 8, 2026 19:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

GUI: Electron segfaults on Ubuntu 26.04 (Wayland) unless --ozone-platform=x11 is set

1 participant