I'm hitting "error: 'builtins.storePath' is not allowed in pure evaluation mode" in my flake, but why?

Hello,

I’m refactoring a flake that uses mach-nix with the goal of making it more customisable, but now I’m hitting a “wall” of the error in the title and I don’t know why.

What I know is that the error happens while evaluating mach-nix’s code and may be related to the way I use it, because I’ve declared it as input to my flake and in order to be able to change the NixPkgs version and the Python interpreter I’m importing it directly in a function code that resembles this:

{
  inputs = {
    mach-nix = {
      url = "github:DavHau/mach-nix/c914064c9b8cab9495818ffe8d834d8c2c1d7ce7";
    };
    flake-utils.url = "github:numtide/flake-utils";
  }
  outputs = { self, flake-utils, mach-nix, nixpkgs}: 
    let
       mkMachNix = {pkgs, pythonVersion}:
         import mach-nix {inherit pkgs; python = pythonVersion};
    # ...

(Note the import mach-nix ... at the end)

What is fun or sad is that I used to do that even before the refactoring but now I’m getting this error. Does anyone have an experience with the error and as any clue of what can I do to “fix” it?
Unfortunately I cannot share the code, but here is the stack trace, maybe it helps a bit:

$ nix --version
nix (Nix) 2.10.3
$ nix develop  --show-trace
warning: Git tree '/home/azazel/wip/xxx/dev-env' is dirty
error: 'builtins.storePath' is not allowed in pure evaluation mode

       at /nix/store/h3r7m7idp56ydbb5dfb1araqjl2fmw61-source/lib/attrsets.nix:335:15:

          334|     let
          335|       path' = builtins.storePath path;
             |               ^
          336|       res =

       … while realising the context of a path

       at /nix/store/7lb0w9fm5zhd66l7cdf571jry48sziq5-source/mach_nix/nix/deps-db-and-fetcher.nix:13:44:

           12| let
           13|   pypi_fetcher_commit = removeSuffix "\n" (readFile "${deps_db_src}/PYPI_FETCHER_COMMIT");
             |                                            ^
           14|   pypi_fetcher_sha256 = removeSuffix "\n" (readFile "${deps_db_src}/PYPI_FETCHER_SHA256");

       … while evaluating 'removeSuffix'

       at /nix/store/h3r7m7idp56ydbb5dfb1araqjl2fmw61-source/lib/strings.nix:486:5:

          485|     # Input string
          486|     str:
             |     ^
          487|     let

       … from call site

       at /nix/store/7lb0w9fm5zhd66l7cdf571jry48sziq5-source/mach_nix/nix/deps-db-and-fetcher.nix:13:25:

           12| let
           13|   pypi_fetcher_commit = removeSuffix "\n" (readFile "${deps_db_src}/PYPI_FETCHER_COMMIT");
             |                         ^
           14|   pypi_fetcher_sha256 = removeSuffix "\n" (readFile "${deps_db_src}/PYPI_FETCHER_SHA256");

       … while realising the context of a path

       at /nix/store/7lb0w9fm5zhd66l7cdf571jry48sziq5-source/mach_nix/nix/deps-db-and-fetcher.nix:20:18:

           19|   };
           20|   pypi_fetcher = import pypi_fetcher_src {
             |                  ^
           21|     inherit pkgs;

       … while evaluating the attribute 'pypi_fetcher'

       at /nix/store/7lb0w9fm5zhd66l7cdf571jry48sziq5-source/mach_nix/nix/deps-db-and-fetcher.nix:30:10:

           29|   '';
           30|   inherit
             |          ^
           31|     pypi_fetcher_src

       … while evaluating 'extendDerivation'

       at /nix/store/h3r7m7idp56ydbb5dfb1araqjl2fmw61-source/lib/customisation.nix:144:43:

          143|      the derivation itself and check a given condition when evaluating. */
          144|   extendDerivation = condition: passthru: drv:
             |                                           ^
          145|     let

       … from call site

       at /nix/store/h3r7m7idp56ydbb5dfb1araqjl2fmw61-source/pkgs/stdenv/generic/make-derivation.nix:378:1:

          377|
          378| lib.extendDerivation
             | ^
          379|   validity.handled

       … while evaluating anonymous lambda

       at /nix/store/h3r7m7idp56ydbb5dfb1araqjl2fmw61-source/pkgs/stdenv/generic/make-derivation.nix:24:1:

           23| #   Explanation about derivations in general
           24| {
             | ^
           25|

       … from call site

       at /nix/store/h3r7m7idp56ydbb5dfb1araqjl2fmw61-source/pkgs/stdenv/generic/make-derivation.nix:381:25:

          380|   ({
          381|      overrideAttrs = f: stdenv.mkDerivation (attrs // (f attrs));
             |                         ^
          382|

       … while evaluating 'overrideAttrs'

       at /nix/store/h3r7m7idp56ydbb5dfb1araqjl2fmw61-source/pkgs/stdenv/generic/make-derivation.nix:381:22:

          380|   ({
          381|      overrideAttrs = f: stdenv.mkDerivation (attrs // (f attrs));
             |                      ^
          382|

       … from call site

       at /nix/store/h3r7m7idp56ydbb5dfb1araqjl2fmw61-source/lib/customisation.nix:86:32:

           85|           ${if result ? overrideAttrs then "overrideAttrs" else null} = fdrv:
           86|             overrideResult (x: x.overrideAttrs fdrv);
             |                                ^
           87|         }

       … while evaluating anonymous lambda

       at /nix/store/h3r7m7idp56ydbb5dfb1araqjl2fmw61-source/lib/customisation.nix:86:29:

           85|           ${if result ? overrideAttrs then "overrideAttrs" else null} = fdrv:
           86|             overrideResult (x: x.overrideAttrs fdrv);
             |                             ^
           87|         }

       … from call site

       at /nix/store/h3r7m7idp56ydbb5dfb1araqjl2fmw61-source/lib/customisation.nix:79:60:

           78|       # Change the result of the function call by applying g to it
           79|       overrideResult = g: makeOverridable (copyArgs (args: g (f args))) origArgs;
             |                                                            ^
           80|     in

       … while evaluating anonymous lambda

       at /nix/store/h3r7m7idp56ydbb5dfb1araqjl2fmw61-source/lib/customisation.nix:79:54:

           78|       # Change the result of the function call by applying g to it
           79|       overrideResult = g: makeOverridable (copyArgs (args: g (f args))) origArgs;
             |                                                      ^
           80|     in

       … from call site

       at /nix/store/h3r7m7idp56ydbb5dfb1araqjl2fmw61-source/lib/customisation.nix:69:16:

           68|     let
           69|       result = f origArgs;
             |                ^
           70|

       … while evaluating 'makeOverridable'

       at /nix/store/h3r7m7idp56ydbb5dfb1araqjl2fmw61-source/lib/customisation.nix:67:24:

           66|   */
           67|   makeOverridable = f: origArgs:
             |                        ^
           68|     let

       … from call site

       at /nix/store/h3r7m7idp56ydbb5dfb1araqjl2fmw61-source/lib/customisation.nix:79:27:

           78|       # Change the result of the function call by applying g to it
           79|       overrideResult = g: makeOverridable (copyArgs (args: g (f args))) origArgs;
             |                           ^
           80|     in

       … while evaluating 'overrideResult'

       at /nix/store/h3r7m7idp56ydbb5dfb1araqjl2fmw61-source/lib/customisation.nix:79:24:

           78|       # Change the result of the function call by applying g to it
           79|       overrideResult = g: makeOverridable (copyArgs (args: g (f args))) origArgs;
             |                        ^
           80|     in

       … from call site

       at /nix/store/h3r7m7idp56ydbb5dfb1araqjl2fmw61-source/lib/customisation.nix:86:13:

           85|           ${if result ? overrideAttrs then "overrideAttrs" else null} = fdrv:
           86|             overrideResult (x: x.overrideAttrs fdrv);
             |             ^
           87|         }

       … while evaluating 'overrideAttrs'

       at /nix/store/h3r7m7idp56ydbb5dfb1araqjl2fmw61-source/lib/customisation.nix:85:73:

           84|           overrideDerivation = fdrv: overrideResult (x: overrideDerivation x fdrv);
           85|           ${if result ? overrideAttrs then "overrideAttrs" else null} = fdrv:
             |                                                                         ^
           86|             overrideResult (x: x.overrideAttrs fdrv);

       … from call site

       at /nix/store/7lb0w9fm5zhd66l7cdf571jry48sziq5-source/mach_nix/nix/withDot.nix:15:15:

           14|       drv = if attr == "" then pyEnvBase else pyEnvBase."${attr}";
           15|       pyEnv = drv.overrideAttrs (oa: {
             |               ^
           16|         passthru =

       … while evaluating 'gen'

       at /nix/store/7lb0w9fm5zhd66l7cdf571jry48sziq5-source/mach_nix/nix/withDot.nix:5:15:

            4|   names = pypiFetcher.allNames;
            5|   gen = attr: selected:
             |               ^
            6|     let

       … from call site

       at /nix/store/7lb0w9fm5zhd66l7cdf571jry48sziq5-source/mach_nix/nix/withDot.nix:26:22:

           25|  "pythonWith" = gen "" [];
           26|  "dockerImageWith" = gen "dockerImage" [];
             |                      ^
           27| }

       … while evaluating the attribute 'dockerImageWith'

       at /nix/store/7lb0w9fm5zhd66l7cdf571jry48sziq5-source/mach_nix/nix/withDot.nix:26:2:

           25|  "pythonWith" = gen "" [];
           26|  "dockerImageWith" = gen "dockerImage" [];
             |  ^
           27| }

       … while evaluating the attribute 'value'

       at /nix/store/h3r7m7idp56ydbb5dfb1araqjl2fmw61-source/lib/types.nix:404:60:

          403|           # Push down position info.
          404|           (map (def: mapAttrs (n: v: { inherit (def) file; value = v; }) def.value) defs)));
             |                                                            ^
          405|       emptyValue = { value = {}; };

       … while evaluating 'dischargeProperties'

       at /nix/store/h3r7m7idp56ydbb5dfb1araqjl2fmw61-source/lib/modules.nix:669:25:

          668|   */
          669|   dischargeProperties = def:
             |                         ^
          670|     if def._type or "" == "merge" then

       … from call site

       at /nix/store/h3r7m7idp56ydbb5dfb1araqjl2fmw61-source/lib/modules.nix:598:137:

          597|         defs' = concatMap (m:
          598|           map (value: { inherit (m) file; inherit value; }) (builtins.addErrorContext "while evaluating definitions from `${m.file}':" (dischargeProperties m.value))
             |                                                                                                                                         ^
          599|         ) defs;

       … while evaluating definitions from `<unknown-file>':

       … while evaluating anonymous lambda

       at /nix/store/h3r7m7idp56ydbb5dfb1araqjl2fmw61-source/lib/modules.nix:597:28:

          596|         # Process mkMerge and mkIf properties.
          597|         defs' = concatMap (m:
             |                            ^
          598|           map (value: { inherit (m) file; inherit value; }) (builtins.addErrorContext "while evaluating definitions from `${m.file}':" (dischargeProperties m.value))

       … from call site

       at /nix/store/h3r7m7idp56ydbb5dfb1araqjl2fmw61-source/lib/modules.nix:597:17:

          596|         # Process mkMerge and mkIf properties.
          597|         defs' = concatMap (m:
             |                 ^
          598|           map (value: { inherit (m) file; inherit value; }) (builtins.addErrorContext "while evaluating definitions from `${m.file}':" (dischargeProperties m.value))

       … while evaluating the attribute 'values'

       at /nix/store/h3r7m7idp56ydbb5dfb1araqjl2fmw61-source/lib/modules.nix:710:7:

          709|     in {
          710|       values = concatMap (def: if getPrio def == highestPrio then [(strip def)] else []) defs;
             |       ^
          711|       inherit highestPrio;

       … while evaluating the attribute 'values'

       at /nix/store/h3r7m7idp56ydbb5dfb1araqjl2fmw61-source/lib/modules.nix:611:9:

          610|       in {
          611|         values = defs''';
             |         ^
          612|         inherit (defs'') highestPrio;

       … while evaluating the attribute 'optionalValue'

       at /nix/store/h3r7m7idp56ydbb5dfb1araqjl2fmw61-source/lib/modules.nix:629:5:

          628|
          629|     optionalValue =
             |     ^
          630|       if isDefined then { value = mergedValue; }

       … while evaluating anonymous lambda

       at /nix/store/h3r7m7idp56ydbb5dfb1araqjl2fmw61-source/lib/types.nix:400:86:

          399|       merge = loc: defs:
          400|         mapAttrs (n: v: v.value) (filterAttrs (n: v: v ? value) (zipAttrsWith (name: defs:
             |                                                                                      ^
          401|             (mergeDefinitions (loc ++ [name]) elemType defs).optionalValue

       … from call site

       at /nix/store/h3r7m7idp56ydbb5dfb1araqjl2fmw61-source/lib/attrsets.nix:369:15:

          368|       inherit name;
          369|       value = f name (catAttrs name sets);
             |               ^
          370|     }) names);

       … while evaluating the attribute 'dockerImageWith'

       at /nix/store/h3r7m7idp56ydbb5dfb1araqjl2fmw61-source/lib/attrsets.nix:369:7:

          368|       inherit name;
          369|       value = f name (catAttrs name sets);
             |       ^
          370|     }) names);

       … while evaluating anonymous lambda

       at /nix/store/h3r7m7idp56ydbb5dfb1araqjl2fmw61-source/lib/types.nix:400:51:

          399|       merge = loc: defs:
          400|         mapAttrs (n: v: v.value) (filterAttrs (n: v: v ? value) (zipAttrsWith (name: defs:
             |                                                   ^
          401|             (mergeDefinitions (loc ++ [name]) elemType defs).optionalValue

       … from call site

       at /nix/store/h3r7m7idp56ydbb5dfb1araqjl2fmw61-source/lib/attrsets.nix:128:62:

          127|   filterAttrs = pred: set:
          128|     listToAttrs (concatMap (name: let v = set.${name}; in if pred name v then [(nameValuePair name v)] else []) (attrNames set));
             |                                                              ^
          129|

       … while evaluating anonymous lambda

       at /nix/store/h3r7m7idp56ydbb5dfb1araqjl2fmw61-source/lib/attrsets.nix:128:29:

          127|   filterAttrs = pred: set:
          128|     listToAttrs (concatMap (name: let v = set.${name}; in if pred name v then [(nameValuePair name v)] else []) (attrNames set));
             |                             ^
          129|

       … from call site

       at /nix/store/h3r7m7idp56ydbb5dfb1araqjl2fmw61-source/lib/attrsets.nix:128:18:

          127|   filterAttrs = pred: set:
          128|     listToAttrs (concatMap (name: let v = set.${name}; in if pred name v then [(nameValuePair name v)] else []) (attrNames set));
             |                  ^
          129|

       … while evaluating 'filterAttrs'

       at /nix/store/h3r7m7idp56ydbb5dfb1araqjl2fmw61-source/lib/attrsets.nix:127:23:

          126|   */
          127|   filterAttrs = pred: set:
             |                       ^
          128|     listToAttrs (concatMap (name: let v = set.${name}; in if pred name v then [(nameValuePair name v)] else []) (attrNames set));

       … from call site

       at /nix/store/h3r7m7idp56ydbb5dfb1araqjl2fmw61-source/lib/types.nix:400:35:

          399|       merge = loc: defs:
          400|         mapAttrs (n: v: v.value) (filterAttrs (n: v: v ? value) (zipAttrsWith (name: defs:
             |                                   ^
          401|             (mergeDefinitions (loc ++ [name]) elemType defs).optionalValue

       … while evaluating 'merge'

       at /nix/store/h3r7m7idp56ydbb5dfb1araqjl2fmw61-source/lib/types.nix:399:20:

          398|       check = isAttrs;
          399|       merge = loc: defs:
             |                    ^
          400|         mapAttrs (n: v: v.value) (filterAttrs (n: v: v ? value) (zipAttrsWith (name: defs:

       … from call site

       at /nix/store/h3r7m7idp56ydbb5dfb1araqjl2fmw61-source/lib/types.nix:203:12:

          202|           }.${commonType} or mergeEqualOption;
          203|         in mergeFunction loc defs;
             |            ^
          204|     };

       … while evaluating 'merge'

       at /nix/store/h3r7m7idp56ydbb5dfb1araqjl2fmw61-source/lib/types.nix:169:20:

          168|       check = value: true;
          169|       merge = loc: defs:
             |                    ^
          170|         let

       … from call site

       at /nix/store/h3r7m7idp56ydbb5dfb1araqjl2fmw61-source/lib/types.nix:195:38:

          194|             stringCoercibleSet = mergeOneOption;
          195|             lambda = loc: defs: arg: anything.merge
             |                                      ^
          196|               (loc ++ [ "<function body>" ])

       … while evaluating 'lambda'

       at /nix/store/h3r7m7idp56ydbb5dfb1araqjl2fmw61-source/lib/types.nix:195:33:

          194|             stringCoercibleSet = mergeOneOption;
          195|             lambda = loc: defs: arg: anything.merge
             |                                 ^
          196|               (loc ++ [ "<function body>" ])

       … from call site

       at /nix/store/yfd5inspv0aisqd5yws085wmgq0n9my4-source/lib.nix:36:8:

           35|                   python ? defaultPythonVersion}:
           36|        mach-nix { inherit pkgs python pypiData;
             |        ^
           37|                   pypiDataRev = defaultPyPIDepsDB.rev;

       … while evaluating 'mkMachNix'

       at /nix/store/yfd5inspv0aisqd5yws085wmgq0n9my4-source/lib.nix:33:17:

           32|   in rec {
           33|     mkMachNix = { mach-nix ? defaultMachNix,
             |                 ^
           34|                   pypiData ? defaultPyPIDepsDB,

       … from call site

       at /nix/store/yfd5inspv0aisqd5yws085wmgq0n9my4-source/lib.nix:64:19:

           63|         python = pkgs.${pythonVersion};
           64|         machNix = mkMachNix {
             |                   ^
           65|           inherit pypiData;

       … while evaluating 'mkPyEnv'

       at /nix/store/yfd5inspv0aisqd5yws085wmgq0n9my4-source/lib.nix:39:15:

           38|                   pypiDataSha256 = defaultPyPIDepsDB.narHash;};
           39|     mkPyEnv = { machNix ? mkMachNix {},
             |               ^
           40|                 defPyReqs ? defaultPythonReqs,

       … from call site

       at /nix/store/yfd5inspv0aisqd5yws085wmgq0n9my4-source/lib.nix:68:17:

           67|         };
           68|         pyEnv = mkPyEnv { inherit machNix pyMachData; };
             |                 ^
           69|         doitAutomation = automationTool == "doit";

       … while evaluating the attribute 'passAsFile'

       at /nix/store/h3r7m7idp56ydbb5dfb1araqjl2fmw61-source/pkgs/build-support/buildenv/default.nix:77:5:

           76|     # XXX: The size is somewhat arbitrary
           77|     passAsFile = if builtins.stringLength pkgs >= 128*1024 then [ "pkgs" ] else [ ];
             |     ^
           78|   }

       … while evaluating the attribute 'passAsFile' of the derivation 'edev'

       at /nix/store/h3r7m7idp56ydbb5dfb1araqjl2fmw61-source/pkgs/stdenv/generic/make-derivation.nix:205:7:

          204|     // (lib.optionalAttrs (attrs ? name || (attrs ? pname && attrs ? version)) {
          205|       name =
             |       ^
          206|         let

       … while evaluating the attribute 'buildInputs' of the derivation 'nix-shell'

       at /nix/store/h3r7m7idp56ydbb5dfb1araqjl2fmw61-source/pkgs/stdenv/generic/make-derivation.nix:205:7:

          204|     // (lib.optionalAttrs (attrs ? name || (attrs ? pname && attrs ? version)) {
          205|       name =
             |       ^
          206|         let

You can pass --impure to “fix” this but then the flake can access more files and is not fully reproduceable.

I finally got it sorted out, thanks to a similar bug report that appeared on home-manager.

To make it short, during the refactoring I introduced a configuration system borrowed from NixOS modules and in one of the options I declared a type = types.package but it Is meant to contain a flake input. It turns out that during merging of the values lib.attrsets.toDerivation is executed and this in turn calls builtins.storePath, hence the error.