Skip to content

Halt and ask instead of aborting when a package step fails - #4870

Open
sernik77 wants to merge 5 commits into
archlinux:masterfrom
sernik77:pacstrap-error-recovery
Open

sernik77 wants to merge 5 commits into
archlinux:masterfrom
sernik77:pacstrap-error-recovery

Conversation

@sernik77

@sernik77 sernik77 commented Nov 5, 2026 •

Copy link
Copy Markdown

Right now one broken package during pacstrap kills the whole install. You've
partitioned, picked everything, waited for the downloads, and then a single
conflict sends you back 5 minutes. This PR makes archinstall let you choose.

  • pacman output is classified (conflict, missing target, file conflict,
    unsatisfiable dep) and you get fixes that fit: keep one side, drop, rename,
    overwrite, retry. Plus always: drop to a shell and resume, or exit and
    save your config
    .
  • Unanswered prompts fall back to a safe default after 60s. If there isn't
    one, unattended runs abort instead of guessing.
  • No retry loops: it only retries if the package list actually changed.
  • Parses err.worker_log, not str(err). The latter is cut to 500 chars and
    often loses the line naming the conflict.
  • Choosing "Exit" doesn't print the "please file a bug" banner anymore.
  • Anything skipped gets listed again at the end.

Code: lib/error_recovery.py (generic prompt), lib/pacman/errors.py (pure
parser, tested on recorded pacman output), wired into Pacman.strap().

In action

jack2 + pipewire-jack in Additional packages: keep either side, drop
both, or the usual shell/exit. Keeping one also lets pacman replace the other
when an earlier step already installed it:
image

After a failed manual fix in the shell, the same failure shows up again, so
it now offers to skip the remaining packages and carry on:
image

Why: I kept hitting the jack2 / pipewire-jack conflict myself, and losing
a whole install over one package felt wrong. So I spent a few hours of spare
time, doing my part in making Arch a slightly better place :)

AI disclosure: the idea and design are mine; Claude Opus 5.5 wrote the code,
the tests and the first draft of this text. I've reviewed and tested it and
can explain any part of it.

sernik77 and others added 2 commits November 4, 2026 22:07
A failed pacstrap run currently ends the installation. Replace that with a
prompt that describes the failure and offers a way out, so a single bad
package does not cost the user the whole install.

The prompt lives in lib/error_recovery.py and knows nothing about pacman:
callers pass the choices that make sense for their own failure, and the two
that always apply -- resolve it by hand in a shell, or exit -- are appended
automatically. Leaving the shell asks whether to resume; exiting offers to
save the configuration first, which main.py honours. Unanswered prompts fall
back to a caller-declared safe choice after 60 seconds.

Where no choice is safe without a human, `safe=None` makes the prompt wait
rather than guess, and an unattended run aborts instead of picking
something. Conflicts usually arise between transitive dependencies rather
than requested packages, so dropping one from the request list would be a
no-op and the retry would fail identically. Two guards prevent that loop: a
retry only happens when the package list actually changed, and the same
failure signature twice in a row stops offering the same way out.

lib/pacman/errors.py classifies pacman output into conflicts, missing
targets, file conflicts and unsatisfiable dependencies. It does no I/O, so
it is tested against recorded pacman output. It reads err.worker_log rather
than str(err), which is truncated to the last 500 characters and routinely
cuts off the line naming the conflict.

Choosing "Exit" is a decision, not a fault, so Installer.__exit__ skips the
bug report banner for InstallationAbortedError and lets main() report it.

Anything skipped during recovery is re-listed once the installation
finishes, so an auto-continued prompt cannot silently produce an
incomplete system.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
check-docstring-first treats string literals after module constants as extra
module docstrings.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Keys pressed while pacstrap was still running (an Enter or two while
waiting) sat in the terminal buffer and answered the prompt unseen, and
every wrong answer redrew the whole block, stacking copies of the choices.
Seen while testing the archinstoo port of this change.

Flush pending input before each question, and ask again on one line
instead of re-rendering; an empty line just asks again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
sernik77 added a commit to sernik77/archinstoo that referenced this pull request Nov 9, 2026
One conflicting package used to end the whole install: Pacman.ask() tried
its keyring/pacman.conf resets, offered a blind "re-try download?" and then
raised. Port of archlinux/archinstall#4870, adapted to archinstoo.

- pm/errors.py classifies pacman output (conflict, missing target, file
  conflict, unsatisfiable dependency, unknown). No I/O, tested against
  recorded output. It reads err.worker_log, since str(err) keeps only the
  last 500 characters and often loses the line naming the conflict.
- pm/recovery.py is the prompt: the caller's choices plus "resolve in a
  shell" and "exit", which are always there.
- Pacman.strap() keeps the keyring and pacman.conf resets: each runs once,
  before anything is asked. Then the prompt offers what can change the
  outcome: keep one side of a conflict, drop, rename, overwrite, retry.
- A retry only happens when the package list changed, and the same failure
  twice in a row stops offering the same way out, so no loops.
- --silent and a closed stdin stop the install instead of guessing, as
  before. Interactive prompts wait: no timeout picks anything.
- Choosing "Exit" is reported without the bug report banner; the selections
  were saved before the install, so the next run offers to resume them.
- Skipped packages are listed again before the final verdict.
- Host-to-target keeps pacman -S, with a hint that fits it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
sernik77 and others added 2 commits November 9, 2026 20:16
Current pacman names both sides of a conflict as name-pkgver-pkgrel
('pipewire-jack-1:1.6.10-1'). Taken verbatim, neither matched the request
list, so a conflict between two requested packages read as one between
dependencies and only shell/exit were offered. Cut the version, but only
when it looks like one, so names like dotnet-runtime-8.0 stay whole.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
When the package the user chose to drop had already been installed by an
earlier step, dropping it from the request changed nothing: pacman asked
"Remove pipewire-jack? [y/N]", --noconfirm answered no, and the retry
failed the same way. After an explicit keep_first/keep_second choice, retry
with --ask=4, which answers exactly that question with yes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@sernik77

sernik77 commented Nov 10, 2026 •

Copy link
Copy Markdown
Author

Kinda unrelated...

So far this feature was reworked few times, and eventually integrated into Archinstoo in pretty minimal form! :)
I hope this could be any use if you feel like it could be good, but it has to be done differently :)

Feel free to ask any questions or request any changes! :)

You can tell it was designed by Human & coded by AI
by how dumb it's designed & how well it (somehow!) works at the same time ^^

This branch has not been deployed

No deployments
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