Skip to content

fix: restore Linux builds and headless remote setup after Electron 41 upgrade#361

Merged
parsakhaz merged 1 commit into
mainfrom
fix/headless-ozone-platform
Jul 22, 2026
Merged

fix: restore Linux builds and headless remote setup after Electron 41 upgrade#361
parsakhaz merged 1 commit into
mainfrom
fix/headless-ozone-platform

Conversation

@parsakhaz

@parsakhaz parsakhaz commented Jul 22, 2026

Copy link
Copy Markdown
Member

Problem

Two failures, one cause.

Linux release builds have failed since v2.4.31. The build (ubuntu-latest) job fails at Verify packaged headless remote setup, which skips publish-windows, publish-release, and checksums. As a result v2.4.31, v2.4.32 and v2.4.33 are all stuck as draft releases with no Windows installers, no latest.yml (Windows auto-update manifest), and no SHA256SUMS.txt.

Headless remote daemon setup is broken for users on those same versions — this is not only a CI problem.

Root cause

The Electron 37 → 41.10.3 upgrade (#352) landed immediately after v2.4.30, the last green build.

Every headless launch path relied on ELECTRON_OZONE_PLATFORM_HINT=headless. That variable was removed in Electron 38 and is ignored from 39 on. On Electron 37 it worked only as an implementation side-effect: the hint was never validated against an allow-list, so headless passed straight through onto --ozone-platform.

The intended fallback could not cover it either. Electron resolves the Ozone platform in PreEarlyInitialization(), before the main script executes, so app.commandLine.appendSwitch('ozone-platform', 'headless') in index.ts was always too late to select a platform.

With neither mechanism live, Chromium's auto default (the default since Chromium 140 / Electron 38) resolves to Wayland if present, else X11 — and with $DISPLAY unset it exits with Missing X server or $DISPLAY / The platform failed to initialize.

Fix

Pass --ozone-platform=headless --disable-gpu as argv from every headless launch site. An explicit --ozone-platform always wins: the hint path is skipped entirely when the switch is already present.

Site Change
scripts/test-packaged-headless-remote-setup.sh flags on the AppImage invocation (unblocks the build)
main/src/daemon/setupRemoteHost.ts flags in the generated systemd unit / launch command
packages/runpane/src/installers.ts new buildPaneDaemonArgs() prepends flags on Linux
packages/runpane-py/src/runpane/installers.py matching build_pane_daemon_args()
main/src/index.ts drop the no-op ozone-platform switch, keep --disable-gpu (still read late enough), correct the comment
docs/SELF_HOSTED_REMOTE_DAEMON.md document the flags; note the env var is dead

spawnPane / spawn_pane are only used for --remote-setup, never the GUI (which uses launchPaneClient), so prepending headless flags there is safe.

The env var is left in the wrapper environments so older Pane builds keep working.

Verification

  • pnpm typecheck — clean

  • node scripts/test-runpane-contract.js — passes, now including a new npm↔Python parity assertion on the argument builder so the two launch paths cannot drift

  • Main-process tests: 389 passed. The 4 failures are pre-existing local better-sqlite3 native-binding errors, identical on an untouched worktree, and unrelated to this change

  • Proven against the real job. Build & Release does not run on PRs (only on release pushes, v* tags, and workflow_dispatch), so this PR's checks cannot exercise it. I dispatched it against this branch: run 29958580933Verify packaged headless remote setup: success, the step that has failed on every release since v2.4.31. All builds completed on macOS, Linux, and both Windows arches.

    All four build jobs in that run then failed at the same later step, upload-artifact, with The artifact name is not valid: ... Contains the following character: Forward slash / — because the artifact name embeds github.ref_name and this branch is fix/headless-ozone-platform. Tags (v2.4.34) contain no slash, so this cannot affect a release. See follow-up below.

Follow-up (not in this PR)

  1. Artifact names break on slash branches. name: pane-${{ matrix.os }}-${{ github.ref_name }} cannot be uploaded from any branch containing /, which makes workflow_dispatch unusable for validating the release pipeline before cutting a tag — exactly what was needed here. Worth sanitizing the slug (or using github.run_id), but it touches the upload/download pairing across four jobs, so it is kept out of this fix.
  2. v2.4.31–v2.4.33 are incomplete drafts with no Windows installers, no latest.yml, and no SHA256SUMS.txt. Their tags point at commits without this fix, so re-running the workflow on them will not help; completing distribution needs a fresh tag once this merges.

Linux release builds have failed since v2.4.31, and headless remote daemon
setup has been broken for users on the same versions. Both regressed with the
Electron 37 -> 41 upgrade (#352).

Electron removed ELECTRON_OZONE_PLATFORM_HINT in 38 and ignores it from 39 on,
so the variable every headless launch path relied on became a no-op. Ozone also
resolves its platform before the app's main script runs, so the
app.commandLine.appendSwitch('ozone-platform', 'headless') fallback in index.ts
could never have selected headless either. With neither mechanism live,
Chromium's auto default falls back to X11 and exits with "Missing X server or
$DISPLAY".

Pass --ozone-platform=headless --disable-gpu as argv from every headless launch
site instead: the CI verification script, the generated systemd unit, and both
the npm and PyPI wrappers. --disable-gpu stays in index.ts because it is read
late enough to still apply. The env var is left in the wrapper environments so
older Pane builds keep working.

Contract parity now covers the argument builder in both wrappers, so the npm and
Python launch paths cannot drift.
@parsakhaz
parsakhaz merged commit 4079ab2 into main Jul 22, 2026
22 of 26 checks passed
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.

1 participant