Skip to content

fix(luks): surface cryptsetup error output when unlock fails - #4570

Merged
svartkanin merged 1 commit into
archlinux:masterfrom
0xdeadd:fix/luks-unlock-error-output
Jun 6, 2026
Merged

fix(luks): surface cryptsetup error output when unlock fails#4570
svartkanin merged 1 commit into
archlinux:masterfrom
0xdeadd:fix/luks-unlock-error-output

Conversation

@0xdeadd

@0xdeadd 0xdeadd commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

What

Luks2.unlock() calls cryptsetup open through run() with no error handling. When the command fails it raises a bare CalledProcessError, and Python's default rendering of that exception prints only the exit status (returned non-zero exit status N) while discarding the captured output. Since run() sets stderr=subprocess.STDOUT, that discarded output is exactly cryptsetup's stderr, so the real diagnostic never reaches install.log.

This wraps the call and raises a DiskError that includes the captured output, mirroring what encrypt() already does a few lines above in the same file:

try:
    result = run(cmd, input_data=passphrase)
except CalledProcessError as err:
    output = err.stdout.decode().rstrip()
    raise DiskError(f'Could not encrypt volume "{self.luks_dev_path}": {output}')

Why

Reported in #4327: a manually partitioned encrypted btrfs install failed at "unlocking luks2 device" with only a traceback ending in returned non-zero exit status 5. The reporter noted that the log did not include the stderr of cryptsetup and had to re-run the command by hand to discover the actual cause (device-mapper: crypt: unknown table type). With this change that message would have been written straight to the install log.

This does not try to fix the underlying device-mapper condition in #4327, which looks environmental and was not reproducible by other contributors. It makes that whole class of unlock failure diagnosable from the log instead of opaque.

Notes

  • Success path is unchanged. Only the failure path differs: a clean DiskError instead of an uncaught CalledProcessError.
  • Consistent with the existing encrypt() error handling in the same module.
  • ruff check and ruff format --check pass.

Luks2.unlock() ran 'cryptsetup open' with no error handling, so a failure raised a bare CalledProcessError. Python renders that exception with only the exit status and discards the captured output, so cryptsetup's stderr (merged into stdout by run()) never reached the install log.

encrypt() already wraps its cryptsetup call and raises a DiskError that includes the captured output. Mirror that for unlock() so a failure reports the actual cryptsetup message instead of an opaque traceback.

Reported in archlinux#4327, where the underlying 'device-mapper: crypt: unknown table type' error was hidden from the log for this reason.
@0xdeadd
0xdeadd requested a review from Torxed as a code owner June 5, 2026 16:14
@svartkanin
svartkanin merged commit 7b8fec1 into archlinux:master Jun 6, 2026
11 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.

2 participants