Skip to content

fix: surface gaia init failures during backend install - #3282

Open
mikemikimike wants to merge 5 commits into
amd:mainfrom
mikemikimike:fix/3206-installer-init-failure
Open

fix: surface gaia init failures during backend install#3282
mikemikimike wants to merge 5 commits into
amd:mainfrom
mikemikimike:fix/3206-installer-init-failure

Conversation

@mikemikimike

Copy link
Copy Markdown
Contributor

Summary\n- fail the backend installer when gaia init --profile minimal --yes exits non-zero\n- preserve the initialization stage and exit code in InstallError so the existing UI shows its Retry dialog\n- add deterministic Electron regression coverage while retaining the explicit skip-init path\n\n## Issue\nCloses #3206\n\n## Validation\n-

pm test -- --runInBand backend-installer-init.test.cjs (2 passed)\n-
pm test -- --runInBand --testPathIgnorePatterns=backend-installer-network.test.cjs (626 passed, 1 skipped)\n-
ode --check for both changed CommonJS files\n- git diff --check\n\nThe existing �ackend-installer-network.test.cjs suite remains blocked on this Windows environment because its OpenSSL certificate-generation setup exits non-zero; that failure is unrelated to this change.

@github-actions github-actions Bot added the tests Test changes label Sep 2, 2026
@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Request changes

Making a failed gaia init stop the install instead of silently reporting success is the right fix for #3206, and the regression test is a good addition. Two things block merge.

The new test only passes on Windows. It hardcodes the Windows virtualenv layout (Scripts/…exe) for the fake binaries it plants, so on Linux the installer never reaches the init stage and both cases fail. The Electron test job runs on Linux, so this turns CI red as written — I ran the suite here and both tests fail; deriving the venv layout from the current platform makes them pass.

"Click Retry" doesn't actually retry the failed step. The error text tells the user to retry, but retrying re-enters the installer, which sees the GAIA package already installed at the pinned version and immediately reports ready — gaia init never runs again. The next app launch does the same and even rewrites the recorded failure back to "ready". So the user gets one loud error, clicks Retry, is told everything succeeded, and still has no models: the exact outcome #3206 is about, one click later. The install needs to remember that initialization didn't finish and re-run it (or the message needs to stop promising a retry that can't happen).

Worth a maintainer decision alongside that: a user whose model download can't succeed (offline, low disk, unsupported hardware) can now no longer get into the app at all, where before they got in without models. Failing loudly matches project policy, so this is a "confirm it's intended" note, not an objection.

Real-world evidence

No evidence bundle was produced for this PR, and the changed surface is the desktop installer's first-launch flow, which this lane can't exercise end to end — so the verdict rests on static review plus the Electron test suite, which I ran directly:

$ npx jest backend-installer-init.test.cjs --runInBand   # on Linux
  ● fails the install and records a retryable gaia-init error
    -   "stage": "gaia-init",
    +   "stage": "install-package",
  ● still supports explicitly skipping gaia init
    Rejected to value: [InstallError: GAIA binary not found at ~/.gaia/venv after install.]
Tests: 2 failed, 2 total

The PR's own validation notes were run on Windows, which is why this didn't show up there. The failure-dialog behaviour (whether Retry recovers) hasn't been exercised on real hardware here and is worth a manual pass on the strix-halo lane before merge.

🔍 Technical details

Issues

🔴 New test is platform-locked and fails CI on Linux (tests/electron/backend-installer-init.test.cjs:34-35)

backend-installer.cjs:48-53 picks Scripts/gaia.exe vs bin/gaia from process.platform, but the test plants only the Windows names. On Linux installBackend throws at backend-installer.cjs:1553 (GAIA binary not found …, stage install-package) before the init stage runs, so the first test's toMatchObject({stage: GAIA_INIT, code: 17}) fails and the second rejects instead of resolving. test_electron.yml runs npm test under tests/electron on ubuntu-latest, so this lands as a red required check.

Verified locally: with the patch below, npx jest backend-installer-init.test.cjs --runInBand goes from 2 failed → 2 passed.

const IS_WINDOWS = process.platform === "win32";
const venvBin = path.join(testHome, ".gaia", "venv", IS_WINDOWS ? "Scripts" : "bin");
const fakePython = path.join(venvBin, IS_WINDOWS ? "python.exe" : "python");
const fakeGaia = path.join(venvBin, IS_WINDOWS ? "gaia.exe" : "gaia");

(beforeAll already mkdirSyncs path.dirname(fakePython), so no other change is needed.)

🟡 The advertised Retry can't re-run gaia init (backend-installer.cjs:1585-1586)

Flow: the throw propagates to ensureBackend, which records STATES.FAILED (:1810-1817) and surfaces the dialog. Choosing Retry loops main.cjs:699-728 back into ensureBackend, whose fast path at :1764-1783 finds findGaiaBin() and installedVersion === expectedVersion, logs "already installed — nothing to do", sets STATES.READY and returns — installBackend (and therefore stage 4) is never reached. main.cjs:668-694 does the same on the next cold start and overwrites the FAILED state with READY.

Options, roughly in order of least churn:

  • Gate the fast path on the previous state: if preChecks.previousState.state === STATES.FAILED && prev.stage === STAGES.GAIA_INIT, run installBackend (or just the init stage) instead of short-circuiting. preChecks.previousState is already read at :1701.
  • Or record an init-completed marker in the state file and require it in both fast paths (ensureBackend:1771 and main.cjs:684), which also covers the "user quit at the dialog" case.

Either way the suggestion text should only promise Retry once Retry does something.

🟢 Fatal-vs-degraded is now a hard gate (backend-installer.cjs:1579)

Previously an init failure still let the app open (unusable chat, but reachable Settings/docs). Now the only exits are Retry, manual-install docs, or Quit. This is consistent with CLAUDE.md's no-silent-fallbacks rule and is likely intended — flagging it only so the change in first-launch UX for offline/unsupported-hardware machines is a conscious call, and because it interacts with the Retry issue above.

Strengths

  • The InstallError carries stage/code/suggestion exactly like the sibling failure sites (:1543, :1601), so the existing failure dialog renders it with no UI changes.
  • The test asserts the absence of the "Lemonade Server setup complete" progress report, not just the throw — that's the actual user-visible symptom in A failed model download produces no error dialog - the installer logs a warning and reports success #3206.
  • Keeping the skipGaiaInit path covered in the same file prevents the fix from quietly breaking CI's GAIA_SKIP_GAIA_INIT installs.

@kovtcharov-amd

Copy link
Copy Markdown
Collaborator

CI has now actually run on this (it was queued behind workflow approval) and it's red — but this one is your PR working as intended.

The appimage-distro-matrix job fails with:

[gaia-init] Installation failed: add-apt-repository not found (required for PPA ...)
[main] Backend bootstrap failed: gaia init exit 1

gaia init has been failing in that container all along because it lacks software-properties-common. The old silent path reported success anyway; your change makes it loud, so a real pre-existing bug now fails the job. Please don't re-silence it.

The fix is either to install that package in the test container, or to drop the PPA dependency from the Linux init path. The second is worth a look — if gaia init needs add-apt-repository, real users on minimal Debian/Ubuntu images hit this too, which is a bigger bug than the CI job.

Log

@kovtcharov-amd kovtcharov-amd left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CI is red — see the comment above. The failure is real, not a flake.

mikemikimike and others added 3 commits September 3, 2026 08:35
The distro matrix was silently failing `gaia init` in every container and
only passing because the installer swallowed the error. With that swallow
removed the job goes red, exposing that the matrix was asserting something
it can never satisfy.

`gaia init` provisions Lemonade, which on Linux ships only via
ppa:lemonade-team/stable. That needs `add-apt-repository`, absent from the
minimal ubuntu:24.04 fixture, and needs apt at all, absent from the fedora
row. Neither container can complete init by construction.

The matrix exists to assert AppImage packaging — FUSE mount, bundled
Chromium libs, sandbox, and the bundled-uv-not-curl backend install. All of
that still runs. The sibling appimage-userns-restricted job already sets
GAIA_SKIP_GAIA_INIT for the same reason; this brings the matrix in line.

Backend provisioning on minimal images is a real user-facing gap and is
tracked separately.
This reverts commit 840d0a0.

Installing software-properties-common only unblocks row 1. Row 3 runs on
fedora:41, which has no apt and therefore can never provide
add-apt-repository, so the matrix would fail on the very next row with the
identical error. Row 1 would also then attempt a live PPA install plus a
model download inside a curl-less container against a 300s readiness poll.

With GAIA_SKIP_GAIA_INIT set the fixture never reaches the PPA path, so the
package is dead weight and its comment contradicts the workflow.
@kovtcharov-amd

Copy link
Copy Markdown
Collaborator

The red CI here is this PR working. It removed the swallow, and a failure that had been hidden all along came out.

gaia init has been failing inside the AppImage distro-matrix containers the whole time — the old Warning: gaia init exited with code 1. Continuing anyway. line meant nobody ever saw it. Same error a user reported in #1217, from the same code path.

The job can't be made to pass by making init work there, though. Row 3 runs on fedora:41, and Lemonade on Linux ships only through a Launchpad PPA, so no amount of package installation gets apt onto a Fedora container. Installing software-properties-common unblocks row 1 and then dies on row 3 with the identical error, so I reverted that commit and took a different route.

What I changed: the matrix now sets GAIA_SKIP_GAIA_INIT=1, matching what the sibling appimage-userns-restricted job already does for the same reason. That job asserts the same contract as these rows — launch, reach state: ready, serve /api/health — and its authors had already decided backend provisioning wasn't part of it. The matrix was just never brought in line, because its init failure was invisible. Everything the rows actually test still runs: FUSE mount, bundled Chromium libs, sandbox, and the bundled-uv-not-curl backend install.

The real bug is now #3340. First-run gaia init dead-ends on any Linux without add-apt-repository — Fedora, Arch, RHEL, and minimal Debian/Ubuntu container/cloud images — and on non-apt distros it tells the user to run an apt command. That's the half of #1217 its fix didn't cover: the localhost probe added there rescues users who already have Lemonade running, not users installing for the first time. Out of scope for this PR, but it's the more important of the two.

🔍 Technical details

Failing run: appimage-distro-matrix. Row 1 hit the 300s readiness poll and dumped:

[gaia-init]    ❌ Installation failed: add-apt-repository not found (required for PPA install).
state: failed (gaia-init)
[main] Backend bootstrap failed: Failed to initialize Lemonade Server and download models (gaia init exit 1).

set -euo pipefail meant row 3 never ran, which is why Fedora didn't show up in the log. It would have failed the same way: _check_linux_version() in lemonade_installer.py only branches on ID=ubuntu and ID=debian, so Fedora falls through returning None and lands on the shutil.which("add-apt-repository") gate in _install_via_ppa.

--env GAIA_SKIP_GAIA_INIT=1 went into DOCKER_RUN_COMMON so all three rows share it. The flag is read by isTruthyEnv(process.env.GAIA_SKIP_GAIA_INIT) in backend-installer.cjs, which gates the stage-4 gaia init block — the same block this PR now throws from. Stage 3 (wheel install via bundled uv) is unaffected, so row 1's purged-curl assertion still means something.

Also corrected the row-1 timeout comment, which justified 300s by the Lemonade + model download that no longer happens.

Two commits: the workflow change, and a revert of 840d0a0 with the reasoning in its message. Net diff against 78ac25a4 is the workflow file only.

Verified --env inside the bash array parses (there was already a comment in that array, so the syntax was proven), and that the YAML still loads and the step still contains the flag. python util/lint.py --all has a pre-existing black failure in this environment — uvx can't fetch the tool — which reproduces on the unmodified branch; the change is YAML-only.

@mikemikimike

Copy link
Copy Markdown
Contributor Author

Implemented the requested CI-container fix in commit 840d0a0.

The appimage-distro-matrix test image now installs software-properties-common before adding the Ubuntu PPA. This restores the add-apt-repository dependency used by the existing gaia init path, while keeping the new init-failure propagation behavior enabled.

Validation:

  • Electron focused Jest regression: 3 passed
  • Electron suite: 19 suites, 627 passed, 1 skipped (628 total)
  • git diff --check passed
  • Docker build was started and apt selected software-properties-common successfully; the build was stopped after the external Ubuntu mirror stalled while downloading large LLVM/Mesa packages. No Docker syntax or package-resolution error was observed.

GitHub Actions checks are pending.

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

Labels

devops DevOps/infrastructure changes tests Test changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants