nixpkgs icon indicating copy to clipboard operation
nixpkgs copied to clipboard

Improve error messages by removing `checkDependencyList` in `stdenv.mkDerivation`

Open 9999years opened this issue 4 months ago • 1 comments

I recently encountered this error attempting to build a derivation:

error:
       … while calling the 'derivationStrict' builtin

         at /builtin/derivation.nix:9:12: (source not available)

       … while evaluating derivation 'infra-shell'
         whose name attribute is located at /nix/store/ppm74s0slima7385piksmdcnvcawgs1x-source/pkgs/stdenv/generic/make-derivation.nix:353:7

       … while evaluating attribute 'nativeBuildInputs' of derivation 'infra-shell'

         at /nix/store/ppm74s0slima7385piksmdcnvcawgs1x-source/pkgs/stdenv/generic/make-derivation.nix:397:7:

          396|       depsBuildBuild              = elemAt (elemAt dependencies 0) 0;
          397|       nativeBuildInputs           = elemAt (elemAt dependencies 0) 1;
             |       ^
          398|       depsBuildTarget             = elemAt (elemAt dependencies 0) 2;

       error: Dependency is not of a valid type: element 23 of nativeBuildInputs for infra-shell

This isn't very helpful; it doesn't tell me what the type of the dependency is. I'm also not directly constructing stdenv.mkDerivation { nativeBuildInputs = [...]; }, so it's not obvious what "element 23" would be (the derivation is constructed through several layers of helpers, like many derivations in nixpkgs are).

Ultimately, element 23 was a function, which surfaces another issue:

nix-repl> throw "Dependency is not of a valid type: ${a: a}"
error:
       … while evaluating a path segment

         at «string»:1:43:

            1| throw "Dependency is not of a valid type: ${a: a}"
             |                                           ^

       error: cannot coerce a function to a string

(builtins.toString produces similar results.) The Nix language doesn't provide a safe value printing function, so we can't really construct a good error message here.

The good news is that we can remove this error checking code from nixpkgs entirely without losing usability. Here's the error message on Nix 2.19.3:

error:
       … while calling the 'derivationStrict' builtin

         at /derivation-internal.nix:9:12:

            8|
            9|   strict = derivationStrict drvAttrs;
             |            ^
           10|

       … while evaluating derivation 'my-derivation'
         whose name attribute is located at /Users/wiggles/nixpkgs/pkgs/stdenv/generic/make-derivation.nix:347:7

       … while evaluating attribute 'nativeBuildInputs' of derivation 'my-derivation'

         at /Users/wiggles/nixpkgs/pkgs/stdenv/generic/make-derivation.nix:391:7:

          390|       depsBuildBuild              = elemAt (elemAt dependencies 0) 0;
          391|       nativeBuildInputs           = elemAt (elemAt dependencies 0) 1;
             |       ^
          392|       depsBuildTarget             = elemAt (elemAt dependencies 0) 2;

       error: cannot coerce a function to a string

Thanks to PRs like https://github.com/NixOS/nix/pull/9754, these error messages will improve in coming Nix releases:

       … while evaluating attribute 'nativeBuildInputs' of derivation 'my-derivation'
         at /Users/wiggles/nixpkgs/pkgs/stdenv/generic/make-derivation.nix:391:7:
          390|       depsBuildBuild              = elemAt (elemAt dependencies 0) 0;
          391|       nativeBuildInputs           = elemAt (elemAt dependencies 0) 1;
             |       ^
          392|       depsBuildTarget             = elemAt (elemAt dependencies 0) 2;

       … while evaluating one element of the list

       error: cannot coerce a function to a string: «lambda @ /Users/wiggles/nixpkgs/xxx-derivation.nix:7:28»

(This shows the exact location of the failing value!)

Description of changes

Things done

  • Built on platform(s)
    • [ ] x86_64-linux
    • [ ] aarch64-linux
    • [ ] x86_64-darwin
    • [x] aarch64-darwin
  • For non-Linux: Is sandboxing enabled in nix.conf? (See Nix manual)
    • [ ] sandbox = relaxed
    • [ ] sandbox = true
  • [ ] Tested, as applicable:
  • [ ] Tested compilation of all packages that depend on this change using nix-shell -p nixpkgs-review --run "nixpkgs-review rev HEAD". Note: all changes have to be committed, also see nixpkgs-review usage
  • [ ] Tested basic functionality of all binary files (usually in ./result/bin/)
  • 24.05 Release Notes (or backporting 23.05 and 23.11 Release notes)
    • [ ] (Package updates) Added a release notes entry if the change is major or breaking
    • [ ] (Module updates) Added a release notes entry if the change is significant
    • [ ] (Module addition) Added a release notes entry if adding a new NixOS module
  • [ ] Fits CONTRIBUTING.md.

Add a :+1: reaction to pull requests you find important.

9999years avatar Mar 05 '24 18:03 9999years

Hey thanks for this PR !

The fix to pict-rs can be merged immediately, but the patching of stdenv needs to go through a lot more review and discussion. Would you mind splitting this PR in two ? Im happy to merge the pictrs part now and will try to reach out to more people for the stdenv fix.

happysalada avatar Mar 05 '24 22:03 happysalada

Sure, split the pict-rs changes off here: #293608

9999years avatar Mar 05 '24 22:03 9999years

@infinisil ignoring the pict-rs changr, there is a proposition of a fix to stdenv, what do you think ?

happysalada avatar Mar 06 '24 21:03 happysalada

How is the error for Nix 2.3 users? That's the minimum version that Nixpkgs supports.

infinisil avatar Mar 12 '24 00:03 infinisil

@infinisil Looks OK:

error: while evaluating the attribute 'buildInputs' of the derivation 'my-derivation' at /Users/wiggles/nixpkgs/pkgs/stdenv/generic/make-derivation.nix:347:7:
cannot coerce a function to a string, at /Users/wiggles/nixpkgs/pkgs/stdenv/generic/make-derivation.nix:347:7

9999years avatar Mar 12 '24 01:03 9999years

While we should worry about older Nix versions too, I don't think this is worth it even for newer Nix versions. See also https://github.com/NixOS/nixpkgs/issues/24462 and the original motivation from https://unix.stackexchange.com/questions/350997/in-a-new-nixos-derivation-based-on-a-binary-distribution-why-am-i-getting-an-e?stw=2

Even if newer Nix versions show the location better, the underlying error message seems worse:

cannot coerce a X to a string

Compared to the old

Dependency is not of a valid type: element 23 of nativeBuildInputs for infra-shell

Especially for newcomers it's not easy to figure out why something even gets coerced to a string in the first place (and what "coerce" even means!).

While the custom error message isn't perfect either, we could easily improve it, e.g. by saying what the actual type is and what it should be, additionally printing a bit of the value (e.g. like the module system, though I also wrote this nice streaming toPretty at some point which would work better]).

infinisil avatar Mar 12 '24 02:03 infinisil

@infinisil I'm not sure I understand -- even with Nix 2.3, the error message clearly states the derivation name (my-derivation) and the attribute that fails to coerce (buildInputs). The only thing that's really removed here is element 23, which I'm not convinced is very useful -- derivations are frequently composed through helpers and overrides such that it's not obvious which index corresponds to which element in the source.

Especially for newcomers it's not easy to figure out why something even gets coerced to a string in the first place (and what "coerce" even means!).

I think newcomers will be well-served by the improved coercion error messages in recent Nix versions (see https://github.com/NixOS/nix/pull/9754) as well as the improved source location information available in recent Nix versions. (And newcomers will tend to run recent Nix versions in general.)

Moreover, having the custom-constructed error message in nixpkgs actually prevents Nix from providing a better error message, which is how I discovered this issue in the first place — I saw the bad error message, attempted to run it with a newer Nix to debug it, and wasn't able to get any additional information from the improvements I've made to error messages. This logic has a cost, and it's not just a maintenance cost!

9999years avatar Mar 12 '24 03:03 9999years

I must say that when you have lists of a hundred packages with some optionals inbetween I usually reside to binary search because not having any information about the wrong element makes it almost impossible to know what to fix even as an advanced nix user. Even just having the name of the package in the error message would eliminate that and save a bunch of time for many people. Now that I know why this is broken, I don't want to keep it like this any longer and do something about it.

SuperSandro2000 avatar Mar 12 '24 07:03 SuperSandro2000

I think it's important to distinguish between developer-facing errors and user-facing errors:

  • User-facing errors are caused by the user using some function in the wrong way. These should be understandable and fixable by just reading a custom error message given with throw and adjusting the function call. Think of these like compiler errors. These messages can be tested as well to ensure that we can guarantee good error messages for certain cases. Users shouldn't have to enable and read stack traces to understand what went wrong, that gets tricky really quickly, especially because error traces can change considerably over time. It's a bit unfortunate how newer Nix versions print part of the stack trace by default, adding a lot of noise but rarely being useful.

  • Developer-facing errors are for mistakes not caused by the user, internal to the function. These can be very hard to fix, so you need a lot of context where --show-trace can help a lot. They need to be fixed by changing the function and it doesn't make sense to test for these.

Check out how lib.fileset does it for example: All user errors are throws with pretty good context to fix the problem.

While it's nice that stack traces now show the location of unexpected values, it would be even better if this would be exposed with e.g. builtins.unsafeGetPos, such that it can be used for all user-facing error messages, instead of functions having to give up on user-facing errors just for that feature.

infinisil avatar Mar 18 '24 18:03 infinisil