Thanks to visit codestin.com
Credit goes to github.com

Skip to content

fix(installer): retry backend install on transient PyPI failures#1561

Merged
kovtcharov-amd merged 3 commits into
mainfrom
fix/installer-retry-transient-pypi
Jun 11, 2026
Merged

fix(installer): retry backend install on transient PyPI failures#1561
kovtcharov-amd merged 3 commits into
mainfrom
fix/installer-retry-transient-pypi

Conversation

@kovtcharov-amd

Copy link
Copy Markdown
Collaborator

Why this matters

Release v0.20.1 failed to publish: the AppImage distro matrix → Row 1 (Ubuntu) smoke test never reached state: ready because the backend bootstrap's uv pip install died mid-download fetching scipy from PyPI — error sending request ... stream closed because of a broken pipe. The installer runs a single install attempt with no retry, so one transient PyPI hiccup failed the whole install and blocked the entire release.

Before: any momentary PyPI/network blip during install fails the AppImage smoke test → release pipeline blocked (and real end-users on flaky networks get a hard install failure). After: the install stage retries transient network failures (3 attempts, linear backoff) and only then fails — loudly, with the same actionable error as before. Deterministic failures (dependency-resolution conflicts, Windows file-lock os error 32) are deliberately not retried.

The local-wheel mechanism (GAIA_LOCAL_WHEEL) only removes the gaia package from the PyPI dependency — the heavy transitive deps (scipy, numpy, torch) are still fetched live, which is the part that flaked.

Test plan

  • node -c backend-installer.cjs — syntax clean
  • isTransientNetworkError validated against the exact v0.20.1 broken-pipe output plus common transient phrasings; confirms it does not match No solution found (resolution) or os error 32 (file lock)
  • cd tests/electron && npm test backend-installer-lock.test.cjs (jest unavailable in my sandbox — needs a runner with deps installed)
  • Re-run the release / AppImage distro matrix to confirm green

The AppImage backend bootstrap runs a single `uv pip install` with no retry.
The gaia wheel is installed from a local file in the release pipeline, but its
heavy transitive deps (scipy, numpy, torch) are still fetched live from PyPI —
so one mid-download network hiccup ("stream closed because of a broken pipe")
fails the entire install. In release v0.20.1 this killed the AppImage distro
matrix smoke test and blocked the whole publish.

Wrap the install stage in a bounded retry (3 attempts, linear backoff) that
fires only on transient network signatures and still fails loudly afterward.
Dependency-resolution errors and Windows file-lock (os error 32) are not
retried — they need user action, not another attempt. This also hardens real
end-user installs on flaky networks, where the install stage does the heaviest
network work.
@github-actions github-actions Bot added the tests Test changes label Jun 10, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Summary

Solid, tightly-scoped resilience fix — Approve. It wraps the single uv pip install call in a 3-attempt linear-backoff retry that fires only on transient network signatures, while deterministic failures (dependency resolution, Windows file-lock os error 32) deliberately fall through to the same loud, actionable error as before. This directly addresses the v0.20.1 publish failure where a mid-download broken pipe fetching scipy killed the AppImage smoke test and blocked the release. The change is fail-loudly-compliant: retries are explicit and bounded, and exhaustion still raises InstallError with the original suggestion text.

I verified isTransientNetworkError against all 14 test cases (the exact v0.20.1 broken-pipe output, common transient phrasings, plus the No solution found / os error 32 / empty / undefined negatives) — all pass. Module syntax is clean (node -c). Jest itself isn't installed in this environment, so the .test.cjs file wasn't executed by a runner, but the underlying logic is confirmed correct.

Issues Found

🟢 Minor — retry detection is one-sided on the combined output (backend-installer.cjs:1204-1208)
The retry gates on isTransientNetworkError(attemptOutput) && !isFileLockedError(attemptOutput), which is the right defensive layering. Just be aware that if a future uv failure interleaves a transient phrase and a non-retryable cause in the same output, the transient match wins the retry decision (bounded to 3 attempts, so worst case is ~9s of wasted backoff before the real error surfaces). Acceptable as-is — noting it only so the precedence is intentional, not accidental.

🟢 Minor — timeout regex prefix groups are decorative (backend-installer.cjs:41)
/(?:request|operation|connection)?\s*tim(?:ed\s*out|eout)/i — since the prefix group is optional and \s* matches zero, this is effectively "match timeout/timed out anywhere." That's the intended broad behavior, but the alternation prefix doesn't narrow anything. Harmless; could be simplified to /tim(?:ed\s*out|eout)/i if you want the regex to read as what it actually does.

Strengths

  • Correctly distinguishes transient vs. deterministic failures — the negative test cases (No solution found, os error 32) lock in that retrying-the-unretryable doesn't regress, which is the subtle part of this kind of fix.
  • Fail-loudly preserved — exhausting retries still throws with the same actionable suggestion and manual-install hint; no silent degradation.
  • Test covers the real-world signature, not just synthetic strings — the broken-pipe case mirrors the actual v0.20.1 output, and the new export is wired into the existing test module cleanly.
  • PR description leads with user-observable impact (release blocked → install survives a PyPI blip) and is honest about what wasn't run.

Verdict

Approve. No blocking issues. The two notes are optional polish. Before merge, it's worth getting the jest suite green in CI (the author flagged it couldn't run locally) and re-running the AppImage distro matrix to confirm the original failure is resolved end-to-end — both already listed as unchecked items in the test plan.

@kovtcharov-amd kovtcharov-amd enabled auto-merge June 11, 2026 00:28
@kovtcharov-amd kovtcharov-amd added this pull request to the merge queue Jun 11, 2026
Merged via the queue into main with commit b4d87a8 Jun 11, 2026
59 checks passed
@kovtcharov-amd kovtcharov-amd deleted the fix/installer-retry-transient-pypi branch June 11, 2026 00:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

tests Test changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants