guix substitute crashes, instead of printing a useful error message, when the only substitutes on offer use unsupported compression

  • Open
  • quality assurance status badge
One participant
  • Zack Weinberg
Submitted by
Zack Weinberg
Zack Weinberg wrote on 12 Mar 01:55 +0100
(address .
`narinfo-best-uri` ends with

(match (sort choices (if fast-decompression? (negate speed<?) file-size<?))
(((uri compression file-size) _ ...)
(values uri compression file-size)))

This will throw an error if the `choices` list is empty. Nothing catches this error, so, if the choices list should happen to be empty for some package, you will get a giant, unhelpful backtrace:

$ guix package -i [PACKAGE]
substitute: Backtrace:
substitute: In ice-9/boot-9.scm:
substitute: 1752:10 17 (with-exception-handler _ _ #:unwind? _ # _)
substitute: In unknown file:
substitute: 16 (apply-smob/0 #<thunk 7f93056f32e0>)
substitute: In ice-9/boot-9.scm:
substitute: 724:2 15 (call-with-prompt _ _ #<procedure default-prompt-handle…>)
substitute: In ice-9/eval.scm:
substitute: 619:8 14 (_ #(#(#<directory (guile-user) 7f93056f8c80>)))
substitute: In guix/ui.scm:
substitute: 2275:7 13 (run-guix . _)
substitute: 2238:10 12 (run-guix-command _ . _)
substitute: In ice-9/boot-9.scm:
substitute: 1752:10 11 (with-exception-handler _ _ #:unwind? _ # _)
substitute: 1752:10 10 (with-exception-handler _ _ #:unwind? _ # _)
substitute: In guix/scripts/substitute.scm:
substitute: 842:18 9 (_)
substitute: 352:7 8 (process-query #<output: file 4> _ #:cache-urls _ #:acl _)
substitute: In srfi/srfi-1.scm:
substitute: 634:9 7 (for-each #<procedure 7f9305761040 at guix/scripts/sub…> …)
substitute: In guix/scripts/substitute.scm:
substitute: 297:13 6 (_ #<<narinfo> path: "/gnu/store/…>)
substitute: In guix/narinfo.scm:
substitute: 346:2 5 (narinfo-best-uri _ #:fast-decompression? _)
substitute: In ice-9/boot-9.scm:
substitute: 1685:16 4 (raise-exception _ #:continuable? _)
substitute: 1685:16 3 (raise-exception _ #:continuable? _)
substitute: 1780:13 2 (_ #<&compound-exception components: (#<&error> #<&orig…>)
substitute: 1685:16 1 (raise-exception _ #:continuable? _)
substitute: 1685:16 0 (raise-exception _ #:continuable? _)
substitute: ice-9/boot-9.scm:1685:16: In procedure raise-exception:
substitute: Throw to key `match-error' with args `("match" "no matching pattern" ())'.
guix package: error: `/usr/bin/guix substitute' died unexpectedly

The strongest clue to what is actually wrong is

Throw to key `match-error' with args `("match" "no matching pattern" ())

and you have to know that that () at the end of the error message means `match` was passed an empty list.

OK, so when can the choices list be empty for a package? Simply, when the only substitutes on offer for that package have been compressed with compression methods that the *Guix daemon*'s Guile installation does not support -- for instance, zstd, if the (zstd) module is not available. narinfo-best-uri intentionally filters those URIs out of the choices list, but is not prepared for the possibility that nothing will be left.

I tripped over this with a somewhat heterodox configuration; a third-party substitute server that only offers zstd-compressed nars, plus Guix as foreign package manager on Debian, with the daemon installed from *Debian's* package, which doesn't have a dependency on guile-zstd (*Guix's* package of guix does depend on guile-zstd). Still, I feel that the principle "externally supplied data, no matter how malformed, should never cause a crash" applies.

The requested change, then, is for `guix substitute` to *not* crash if the filtered choices list is empty. Probably narinfo-best-uri should return (values nil nil nil) in that case, and then scripts/substitute.scm should also be changed to error out gracefully and/or fall back to local building when it gets that result from narinfo-best-uri.