Skip to content

Diagnose the retry that follows a deleted package - #131

Open
omarchybot wants to merge 1 commit into
quattrofrom
fix/mirror-retry-diagnosis
Open

Diagnose the retry that follows a deleted package#131
omarchybot wants to merge 1 commit into
quattrofrom
fix/mirror-retry-diagnosis

Conversation

@omarchybot

Copy link
Copy Markdown
Collaborator

The failure screen names the install medium when pacman rejects a package off it, but only on the attempt that does the rejecting. Every attempt after that fails on the open instead: pacstrap runs with --noconfirm, so pacman answers yes to "delete it?" and unlinks the package through the bind mount that makes the ISO's offline mirror the target's package cache. A retry's log carries no is corrupted line, nothing matches, and the screen falls back to the bare pacstrap error with the word USB nowhere on it. That is the state most people are in by the time they read the screen, because the attempt that explained it scrolled away with the first try.

pacman names the repository it read from in that error, not the cache it writes to, so the match keys on the mirror path rather than on the target cache path:

error: failed retrieving file 'gst-plugin-gtk-1.28.6-1-x86_64.pkg.tar.zst' from disk : Could not open file /var/cache/omarchy/mirror/offline/gst-plugin-gtk-1.28.6-1-x86_64.pkg.tar.zst

Where a log holds both attempts the checksum failure still wins. It is the one that names the cause, and the open failures after it are its wake.

The new verdict sends the reader to reboot rather than offering a remedy for the medium. Nothing can be weighed once the bytes are gone, and the deletion landed in the live session's RAM overlay rather than on the stick, so a reboot brings the ISO's copy back and the first error after that is the one that says what actually failed.

Reported in omacom/omarchy#7704, where the undiagnosed retry is the exact error two people are stuck on. This does not stop the deletion itself — that is the read-write cache bind in _mount_offline_package_cache, which #108 and #113 are both rewriting.

Codex at xhigh reasoning reviewed the failure and confirmed the gap this closes: a retry log holding only the retrieval error produces no diagnosis, while an accumulated log that still carries the first attempt's line is diagnosed as before, which is the precedence kept here. Its independence is not currently guaranteed.

Every attempt after the first fails on the open rather than on a checksum: pacstrap runs with --noconfirm, so pacman answers yes to "delete it?" and unlinks the package through the bind mount that makes the ISO's offline mirror the target's package cache. The failure screen then carried no diagnosis at all, because the only line it matched -- "is corrupted" under the target cache -- was written by the attempt that already scrolled away. A retry is the state most people are in by the time they read the screen, and it was the one case with nothing on it.

pacman names the repository it read from in that error, not the cache it writes to, so the match keys on the mirror path rather than the target cache path. Where a log holds both attempts the checksum failure still wins: it is the one that names the cause, and the open failures after it are its wake.

The verdict sends the reader to reboot instead of offering a remedy for the medium. Nothing can be weighed once the bytes are gone, and the deletion landed in the live session's RAM overlay rather than on the stick, so a reboot brings the ISO's copy back and the first error after it is the one that says what actually failed.
@omarchybot

Copy link
Copy Markdown
Collaborator Author

Reviewed at da40ed2 against quattro (merge-base 268bac1), twice: once by Claude Opus 5 in Claude Code, and once independently by Codex at xhigh reasoning. ./test/all ran on a disposable Omarchy worker VM rather than on the machine holding credentials, and is green — 18 checks in test/unit/install-media-diagnosis-test.sh including the three added here, plus the release-checksum and partition suites and 63 Python tests. The installer itself was not executed, so nothing below is proved against a real failing stick.

It is genuinely additive. All 95 lines are insertions. corrupt_package still runs first and its three verdicts are unchanged; no existing command was wrapped, no redirect or tee introduced, no command substitution placed around something that used to stream, and the exit statuses are the same (0 with a diagnosis, 1 with none, 2 on usage). deleted=1 at line 101 is set even when deleted_package found nothing, but the empty-package guard two lines later exits first, so no wrong branch can run. The file carries set -uo pipefail without -e, so a failed sed degrades to "no match" rather than aborting and the closing printf returns success either way. Both reviewers went looking for a swallowed non-zero exit; neither found one.

The parsing is right, and that was checked rather than assumed. Against the installed pacman 7.1.0 / libalpm 16.0.1 / libcurl 8.21.0: libalpm carries failed retrieving file '%s' from %s : %s and the bare string disk it substitutes as the host for a file:// server, and libcurl carries Could not open file %s with a capital C. libalpm's own could not open file %s: %s is lowercase, so the capital-C pattern selects curl's file-source error and nothing else. The fixture wording at test/unit/install-media-diagnosis-test.sh:152 is faithful to what pacman actually prints. [^ ]* handles +, an epoch : and percent-encoding correctly.

High — the branch does not fire on the retry it was written for. omarchy-install-diagnose-media:97 deliberately lets a checksum failure in any supplied log win, and the shipped log handling means that history is always still present. .automated_script.sh:44-50 touches the live log and then appends to it through tee, omarchy-install-dashboard:794 appends the installer child's output to the same file, and phases_impl.py:1120 copies the whole live log to the target — nothing rotates, truncates or relocates it. So the second attempt in a boot, which is the retry configurator:181 points the user at (./.automated_script.sh after "Drop to shell"), reads a log that still holds the first attempt's File /mnt/var/cache/pacman/pkg/… is corrupted; corrupt_package matches it and lines 99-101 are never reached. Across a reboot the archiso overlay is RAM — no cow_device= or persistence in configs/grub/grub.cfg, configs/syslinux/archiso_sys-linux.cfg or configs/efiboot/loader/entries/01-archiso-x86_64-linux.conf — so the log and the deletion go together and the next failure is a checksum failure again. The remedy the branch prints is self-defeating on the same reasoning: a reboot is exactly the event that would have restored the package and made the branch unnecessary. The retry-only fixture at test/unit/install-media-diagnosis-test.sh:158 models a log the installer does not produce, and the combined fixture at 173-188 demonstrates that the new verdict loses whenever the real history is there.

Medium — the one path that can fire is the one the message describes wrongly. omarchy-install-diagnose-media:58 accepts any non-space basename under the mirror and establishes neither that the path is a package nor that a checksum rejection preceded it. A first-attempt open failure with no deletion behind it — offline.db, or a stick far enough gone that the open itself errors — reaches lines 116-123 and tells the reader that an earlier attempt deleted the package and that a reboot will bring it back, when nothing deleted it and a reboot restores nothing. Codex put the two findings together this way, which is sharper than either alone: an open-only first failure is the principal realistic shipped trigger for the branch, and it is precisely the case the branch gets wrong. Anchoring the capture on a package filename the way the sibling function in #132 does, \([^ ]*\.pkg\.tar[^ :]*\), rules out offline.db but not the dying stick; that one needs the branch to require evidence of a prior deletion rather than infer it.

On omacom/omarchy#7704, which this exists for. Two things were asked of the reporter by hand: the exact package filename, and the first error in /var/log/omarchy-install.log. The filename is already captured — #120 prints it on its own line in all three existing verdicts, and by the finding above that is what fires on the first failure and on the retry too, so this PR adds filename extraction only for the fallback that does not fire. The first error is not captured at all: nothing here quotes a log line, it extracts a basename and prints canned text, and the failure screen still shows only the last handful of log lines (omarchy-install-dashboard:573-603), so the reporter is still left viewing or uploading the full log to find the first error by hand. If surfacing that first error is the goal, it is a different change — print the first matching pacman error rather than the last. And worth saying plainly either way: neither #120 nor this is in the 4.0.0 or 4.0.1 image the people in that issue are installing from, so nothing merged here reaches them before a new ISO is cut.

Sequencing, for whoever decides the order. #132 edits this same file in this same region: it inserts unreadable_package() between corrupt_package() and expected_checksum() and extends the same fallback chain with an early-print branch in the same place, so a textual conflict is near certain. More than that, #132 makes the offline mirror a read-only mount (var-cache-omarchy-mirror-offline.mount), which is the point its own header comment makes — "the package survives its own rejection" — and that removes the deletion this diagnoses. #113 leaves _mount_offline_package_cache and its read-write bind untouched so the deletion survives there, but it replaces the 925-package pacstrap with a btrfs image plus a small delta pacstrap, which shrinks the exposure to a handful of packages. #110 is the boot menu, a different stage; no overlap.

Waiting on the author for a concrete reachable path in the shipped flow, or for a narrower change. Nothing was pushed to the branch: the first finding is a design question rather than a defect with an obvious patch, and tightening the regex for the second is not worth doing while the branch it guards is in question.

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