Skip to content

top-level/stage: assert no by-name overwrites#454525

Closed
wolfgangwalther wants to merge 1 commit intoNixOS:masterfrom
wolfgangwalther:by-name-assert-no-overrides
Closed

top-level/stage: assert no by-name overwrites#454525
wolfgangwalther wants to merge 1 commit intoNixOS:masterfrom
wolfgangwalther:by-name-assert-no-overrides

Conversation

@wolfgangwalther
Copy link
Contributor

This asserts that no by-name packages are overwritten by definitions in all-packages.nix.

The relevant code from

{
# This attribute is necessary to allow CI to ensure that all packages defined in `pkgs/by-name`
# don't have an overriding definition in `all-packages.nix` with an empty (`{ }`) second `callPackage` argument.
# It achieves that with an overlay that modifies both `callPackage` and this attribute to signal whether `callPackage` is used
# and whether it's defined by this file here or `all-packages.nix`.
# TODO: This can be removed once `pkgs/by-name` can handle custom `callPackage` arguments without `all-packages.nix` (or any other way of achieving the same result).
# Because at that point the code in ./stage.nix can be changed to not allow definitions in `all-packages.nix` to override ones from `pkgs/by-name` anymore and throw an error if that happens instead.
_internalCallByNamePackageFile = file: self.callPackage file { };
}
can only be removed once nixpkgs-vet has removed the check, too.

(replacement of #454147)

Things done


Add a 👍 reaction to pull requests you find important.

This asserts that no by-name packages are overwritten by definitions in
all-packages.nix.
@philiptaron
Copy link
Contributor

We got into more trouble with this than I expected.

I tried to make this show the meta.position of the two cases, but ran into infinite recursion. Sad.

@MattSturgeon
Copy link
Contributor

MattSturgeon commented Oct 24, 2025

I tried to make this show the meta.position of the two cases, but ran into infinite recursion.

The issue here is that the assertion is thown part-way through fixing the various overlays that'll construct self, while the packages themselves almost always depend on self.

Therefore it is safe to evaluate the attrnames, but evaluating a package value will require self, which can't be done when one of the overlays throws.

One way to work-around this is to move the assert/throw outside of the overlay chain, so that self can be reliably evaluated:

diff --git a/pkgs/top-level/stage.nix b/pkgs/top-level/stage.nix
index 12d3b9315e9a..be952b18aaad 100644
--- a/pkgs/top-level/stage.nix
+++ b/pkgs/top-level/stage.nix
@@ -189,9 +189,19 @@ let
 
       conflictingAttrs = lib.intersectAttrs res super;
     in
-    assert lib.assertMsg (conflictingAttrs == { })
-      "The following attributes were defined both in `pkgs/top-level/all-packages.nix` and elsewhere, most likely in `pkgs/by-name/`: ${lib.concatStringsSep ", " (lib.attrNames conflictingAttrs)}";
-    res;
+    res
+    // {
+      __assertions = res.__assertions or [ ] ++ [
+        {
+          assertion = conflictingAttrs == { };
+          message = "The following attributes were defined both in `pkgs/top-level/all-packages.nix` and elsewhere, most likely in `pkgs/by-name/`: ${
+            lib.concatMapAttrsStringSep "\n" (
+              name: pkg: "- ${name}" + lib.optionalString (pkg ? meta.position) ": ${pkg.meta.position}"
+            ) conflictingAttrs
+          }";
+        }
+      ];
+    };
 
   aliases = self: super: lib.optionalAttrs config.allowAliases (import ./aliases.nix lib self super);
 
@@ -357,6 +367,8 @@ let
     ]
   );
 
+  handleAssertions =
+    result: lib.asserts.checkAssertWarn (result.__assertions or [ ]) (result.__warnings or [ ]) result;
 in
-# Return the complete set of packages.
-lib.fix toFix
+# Check assertions and return the complete set of packages.
+handleAssertions (lib.fix toFix)

This solves the inf-rec, but I'm seeing another weird error about packages being missing from callPackage autoArgs:

$ nix-instantiate --attr hello
error:
       … while evaluating a branch condition
         at /home/matt/nixpkgs/ci/pkgs/stdenv/booter.nix:107:9:
          106|       thisStage =
          107|         if args.__raw or false then
             |         ^
          108|           args'

       … in the right operand of the update (//) operator
         at /home/matt/nixpkgs/ci/pkgs/stdenv/booter.nix:90:44:
           89|     # for the final stage.
           90|     { allowCustomOverrides = index == 1; } // (stageFun prevStage)
             |                                            ^
           91|   ) (lib.lists.reverseList stageFuns);

       (stack trace truncated; use '--show-trace' to show the full, detailed trace)

       error: evaluation aborted with the following error message: 'lib.customisation.callPackageWith: Function called without required argument "bazel_self" at /home/matt/nixpkgs/ci/pkgs/by-name/ba/bazel_7/package.nix:15'
Full trace

$ nix-instantiate --attr hello --show-trace
error:
       … from call site
         at /home/matt/nixpkgs/ci/pkgs/top-level/impure.nix:12:1:
           11|
           12| {
             | ^
           13|   # We put legacy `system` into `localSystem`, if `localSystem` was not passed.

       … while calling anonymous lambda
         at /home/matt/nixpkgs/ci/pkgs/top-level/impure.nix:12:1:
           11|
           12| {
             | ^
           13|   # We put legacy `system` into `localSystem`, if `localSystem` was not passed.

       … from call site
         at /home/matt/nixpkgs/ci/pkgs/top-level/impure.nix:57:1:
           56|
           57| import ./. (
             | ^
           58|   removeAttrs args [ "system" ]

       … while calling anonymous lambda
         at /home/matt/nixpkgs/ci/pkgs/top-level/default.nix:21:1:
           20|
           21| {
             | ^
           22|   # The system packages will be built on. See the manual for the

       … from call site
         at /home/matt/nixpkgs/ci/pkgs/top-level/default.nix:190:1:
          189| in
          190| checked pkgs
             | ^
          191|

       … while calling anonymous lambda
         at /home/matt/nixpkgs/ci/pkgs/top-level/default.nix:91:8:
           90|       (x: throwIfNot (lib.isFunction x) "All crossOverlays passed to nixpkgs must be functions.")
           91|       (r: r)
             |        ^
           92|       crossOverlays;

       … from call site
         at /home/matt/nixpkgs/ci/pkgs/top-level/default.nix:187:10:
          186|
          187|   pkgs = boot stages;
             |          ^
          188|

       … while calling anonymous lambda
         at /home/matt/nixpkgs/ci/pkgs/stdenv/booter.nix:42:1:
           41| # other words, this does a foldr not foldl.
           42| stageFuns:
             | ^
           43| let

       … from call site
         at /home/matt/nixpkgs/ci/pkgs/stdenv/booter.nix:166:1:
          165| in
          166| dfold folder postStage (_: { }) withAllowCustomOverrides
             | ^
          167|

       … while calling 'dfold'
         at /home/matt/nixpkgs/ci/pkgs/stdenv/booter.nix:63:21:
           62|   dfold =
           63|     op: lnul: rnul: list:
             |                     ^
           64|     let

       … from call site
         at /home/matt/nixpkgs/ci/pkgs/stdenv/booter.nix:78:13:
           77|       lapp = lnul cur;
           78|       cur = go lapp 0;
             |             ^
           79|     in

       … while calling 'go'
         at /home/matt/nixpkgs/ci/pkgs/stdenv/booter.nix:67:15:
           66|       go =
           67|         pred: n:
             |               ^
           68|         if n == len then

       … from call site
         at /home/matt/nixpkgs/ci/pkgs/stdenv/booter.nix:73:19:
           72|             # Note the cycle -- call-by-need ensures finite fold.
           73|             cur = op pred (builtins.elemAt list n) succ;
             |                   ^
           74|             succ = go cur (n + 1);

       … while calling 'folder'
         at /home/matt/nixpkgs/ci/pkgs/stdenv/booter.nix:96:26:
           95|   folder =
           96|     nextStage: stageFun: prevStage:
             |                          ^
           97|     let

       … while evaluating a branch condition
         at /home/matt/nixpkgs/ci/pkgs/stdenv/booter.nix:107:9:
          106|       thisStage =
          107|         if args.__raw or false then
             |         ^
          108|           args'

       … from call site
         at /home/matt/nixpkgs/ci/pkgs/stdenv/booter.nix:98:14:
           97|     let
           98|       args = stageFun prevStage;
             |              ^
           99|       args' = args // {

       … while calling anonymous lambda
         at /home/matt/nixpkgs/ci/pkgs/stdenv/booter.nix:86:22:
           85|   withAllowCustomOverrides = lib.lists.imap1 (
           86|     index: stageFun: prevStage:
             |                      ^
           87|     # So true by default for only the first element because one

       … in the right operand of the update (//) operator
         at /home/matt/nixpkgs/ci/pkgs/stdenv/booter.nix:90:44:
           89|     # for the final stage.
           90|     { allowCustomOverrides = index == 1; } // (stageFun prevStage)
             |                                            ^
           91|   ) (lib.lists.reverseList stageFuns);

       … from call site
         at /home/matt/nixpkgs/ci/pkgs/stdenv/booter.nix:90:48:
           89|     # for the final stage.
           90|     { allowCustomOverrides = index == 1; } // (stageFun prevStage)
             |                                                ^
           91|   ) (lib.lists.reverseList stageFuns);

       … while calling anonymous lambda
         at /home/matt/nixpkgs/ci/pkgs/stdenv/linux/default.nix:937:5:
          936|   (
          937|     prevStage:
             |     ^
          938|     # previous stage5 stdenv; see stage3 comment regarding gcc,

       … in the condition of the assert statement
         at /home/matt/nixpkgs/ci/pkgs/stdenv/linux/default.nix:940:5:
          939|     # which applies here as well.
          940|     assert isBuiltByNixpkgsCompiler prevStage.binutils-unwrapped;
             |     ^
          941|     assert isBuiltByNixpkgsCompiler prevStage.${localSystem.libc};

       … from call site
         at /home/matt/nixpkgs/ci/pkgs/stdenv/linux/default.nix:940:12:
          939|     # which applies here as well.
          940|     assert isBuiltByNixpkgsCompiler prevStage.binutils-unwrapped;
             |            ^
          941|     assert isBuiltByNixpkgsCompiler prevStage.${localSystem.libc};

       … while calling 'isBuiltByNixpkgsCompiler'
         at /home/matt/nixpkgs/ci/pkgs/stdenv/linux/default.nix:131:30:
          130|   isFromBootstrapFiles = pkg: pkg.passthru.isFromBootstrapFiles or false;
          131|   isBuiltByNixpkgsCompiler = pkg: isFromNixpkgs pkg && isFromNixpkgs pkg.stdenv.cc.cc;
             |                              ^
          132|   isBuiltByBootstrapFilesCompiler = pkg: isFromNixpkgs pkg && isFromBootstrapFiles pkg.stdenv.cc.cc;

       … in the left operand of the AND (&&) operator
         at /home/matt/nixpkgs/ci/pkgs/stdenv/linux/default.nix:131:53:
          130|   isFromBootstrapFiles = pkg: pkg.passthru.isFromBootstrapFiles or false;
          131|   isBuiltByNixpkgsCompiler = pkg: isFromNixpkgs pkg && isFromNixpkgs pkg.stdenv.cc.cc;
             |                                                     ^
          132|   isBuiltByBootstrapFilesCompiler = pkg: isFromNixpkgs pkg && isFromBootstrapFiles pkg.stdenv.cc.cc;

       … from call site
         at /home/matt/nixpkgs/ci/pkgs/stdenv/linux/default.nix:131:35:
          130|   isFromBootstrapFiles = pkg: pkg.passthru.isFromBootstrapFiles or false;
          131|   isBuiltByNixpkgsCompiler = pkg: isFromNixpkgs pkg && isFromNixpkgs pkg.stdenv.cc.cc;
             |                                   ^
          132|   isBuiltByBootstrapFilesCompiler = pkg: isFromNixpkgs pkg && isFromBootstrapFiles pkg.stdenv.cc.cc;

       … while calling 'isFromNixpkgs'
         at /home/matt/nixpkgs/ci/pkgs/stdenv/linux/default.nix:129:19:
          128|
          129|   isFromNixpkgs = pkg: !(isFromBootstrapFiles pkg);
             |                   ^
          130|   isFromBootstrapFiles = pkg: pkg.passthru.isFromBootstrapFiles or false;

       … in the argument of the not operator
         at /home/matt/nixpkgs/ci/pkgs/stdenv/linux/default.nix:129:26:
          128|
          129|   isFromNixpkgs = pkg: !(isFromBootstrapFiles pkg);
             |                          ^
          130|   isFromBootstrapFiles = pkg: pkg.passthru.isFromBootstrapFiles or false;

       … from call site
         at /home/matt/nixpkgs/ci/pkgs/stdenv/linux/default.nix:129:26:
          128|
          129|   isFromNixpkgs = pkg: !(isFromBootstrapFiles pkg);
             |                          ^
          130|   isFromBootstrapFiles = pkg: pkg.passthru.isFromBootstrapFiles or false;

       … while calling 'isFromBootstrapFiles'
         at /home/matt/nixpkgs/ci/pkgs/stdenv/linux/default.nix:130:26:
          129|   isFromNixpkgs = pkg: !(isFromBootstrapFiles pkg);
          130|   isFromBootstrapFiles = pkg: pkg.passthru.isFromBootstrapFiles or false;
             |                          ^
          131|   isBuiltByNixpkgsCompiler = pkg: isFromNixpkgs pkg && isFromNixpkgs pkg.stdenv.cc.cc;

       … from call site
         at /home/matt/nixpkgs/ci/pkgs/stdenv/booter.nix:74:20:
           73|             cur = op pred (builtins.elemAt list n) succ;
           74|             succ = go cur (n + 1);
             |                    ^
           75|           in

       (8 duplicate frames omitted)

       … while calling anonymous lambda
         at /home/matt/nixpkgs/ci/pkgs/stdenv/linux/default.nix:747:5:
          746|   (
          747|     prevStage:
             |     ^
          748|     # previous stage4 stdenv; see stage3 comment regarding gcc,

       … in the condition of the assert statement
         at /home/matt/nixpkgs/ci/pkgs/stdenv/linux/default.nix:750:5:
          749|     # which applies here as well.
          750|     assert isBuiltByNixpkgsCompiler prevStage.binutils-unwrapped;
             |     ^
          751|     assert isBuiltByNixpkgsCompiler prevStage.${localSystem.libc};

       … from call site
         at /home/matt/nixpkgs/ci/pkgs/stdenv/linux/default.nix:750:12:
          749|     # which applies here as well.
          750|     assert isBuiltByNixpkgsCompiler prevStage.binutils-unwrapped;
             |            ^
          751|     assert isBuiltByNixpkgsCompiler prevStage.${localSystem.libc};

       (16 duplicate frames omitted)

       … while calling anonymous lambda
         at /home/matt/nixpkgs/ci/pkgs/stdenv/linux/default.nix:672:5:
          671|   (
          672|     prevStage:
             |     ^
          673|     # previous stage3 stdenv:

       … in the condition of the assert statement
         at /home/matt/nixpkgs/ci/pkgs/stdenv/linux/default.nix:674:5:
          673|     # previous stage3 stdenv:
          674|     assert isBuiltByNixpkgsCompiler prevStage.binutils-unwrapped;
             |     ^
          675|     assert isBuiltByNixpkgsCompiler prevStage.${localSystem.libc};

       … from call site
         at /home/matt/nixpkgs/ci/pkgs/stdenv/linux/default.nix:674:12:
          673|     # previous stage3 stdenv:
          674|     assert isBuiltByNixpkgsCompiler prevStage.binutils-unwrapped;
             |            ^
          675|     assert isBuiltByNixpkgsCompiler prevStage.${localSystem.libc};

       (16 duplicate frames omitted)

       … while calling anonymous lambda
         at /home/matt/nixpkgs/ci/pkgs/stdenv/linux/default.nix:599:5:
          598|   (
          599|     prevStage:
             |     ^
          600|     # previous stage2 stdenv:

       … in the condition of the assert statement
         at /home/matt/nixpkgs/ci/pkgs/stdenv/linux/default.nix:601:5:
          600|     # previous stage2 stdenv:
          601|     assert isBuiltByNixpkgsCompiler prevStage.binutils-unwrapped;
             |     ^
          602|     assert isBuiltByNixpkgsCompiler prevStage.${localSystem.libc};

       … from call site
         at /home/matt/nixpkgs/ci/pkgs/stdenv/linux/default.nix:601:12:
          600|     # previous stage2 stdenv:
          601|     assert isBuiltByNixpkgsCompiler prevStage.binutils-unwrapped;
             |            ^
          602|     assert isBuiltByNixpkgsCompiler prevStage.${localSystem.libc};

       (16 duplicate frames omitted)

       … while calling anonymous lambda
         at /home/matt/nixpkgs/ci/pkgs/stdenv/linux/default.nix:494:5:
          493|   (
          494|     prevStage:
             |     ^
          495|     # previous stage1 stdenv:

       … in the condition of the assert statement
         at /home/matt/nixpkgs/ci/pkgs/stdenv/linux/default.nix:496:5:
          495|     # previous stage1 stdenv:
          496|     assert isBuiltByBootstrapFilesCompiler prevStage.binutils-unwrapped;
             |     ^
          497|     assert isFromBootstrapFiles prevStage."${localSystem.libc}";

       … from call site
         at /home/matt/nixpkgs/ci/pkgs/stdenv/linux/default.nix:496:12:
          495|     # previous stage1 stdenv:
          496|     assert isBuiltByBootstrapFilesCompiler prevStage.binutils-unwrapped;
             |            ^
          497|     assert isFromBootstrapFiles prevStage."${localSystem.libc}";

       … while calling 'isBuiltByBootstrapFilesCompiler'
         at /home/matt/nixpkgs/ci/pkgs/stdenv/linux/default.nix:132:37:
          131|   isBuiltByNixpkgsCompiler = pkg: isFromNixpkgs pkg && isFromNixpkgs pkg.stdenv.cc.cc;
          132|   isBuiltByBootstrapFilesCompiler = pkg: isFromNixpkgs pkg && isFromBootstrapFiles pkg.stdenv.cc.cc;
             |                                     ^
          133|

       … in the left operand of the AND (&&) operator
         at /home/matt/nixpkgs/ci/pkgs/stdenv/linux/default.nix:132:60:
          131|   isBuiltByNixpkgsCompiler = pkg: isFromNixpkgs pkg && isFromNixpkgs pkg.stdenv.cc.cc;
          132|   isBuiltByBootstrapFilesCompiler = pkg: isFromNixpkgs pkg && isFromBootstrapFiles pkg.stdenv.cc.cc;
             |                                                            ^
          133|

       … from call site
         at /home/matt/nixpkgs/ci/pkgs/stdenv/linux/default.nix:132:42:
          131|   isBuiltByNixpkgsCompiler = pkg: isFromNixpkgs pkg && isFromNixpkgs pkg.stdenv.cc.cc;
          132|   isBuiltByBootstrapFilesCompiler = pkg: isFromNixpkgs pkg && isFromBootstrapFiles pkg.stdenv.cc.cc;
             |                                          ^
          133|

       (13 duplicate frames omitted)

       … while calling anonymous lambda
         at /home/matt/nixpkgs/ci/pkgs/stdenv/linux/default.nix:379:5:
          378|   (
          379|     prevStage:
             |     ^
          380|     assert isBuiltByBootstrapFilesCompiler prevStage.binutils-unwrapped;

       … in the condition of the assert statement
         at /home/matt/nixpkgs/ci/pkgs/stdenv/linux/default.nix:380:5:
          379|     prevStage:
          380|     assert isBuiltByBootstrapFilesCompiler prevStage.binutils-unwrapped;
             |     ^
          381|     assert isFromBootstrapFiles prevStage."${localSystem.libc}";

       … from call site
         at /home/matt/nixpkgs/ci/pkgs/stdenv/linux/default.nix:380:12:
          379|     prevStage:
          380|     assert isBuiltByBootstrapFilesCompiler prevStage.binutils-unwrapped;
             |            ^
          381|     assert isFromBootstrapFiles prevStage."${localSystem.libc}";

       (16 duplicate frames omitted)

       … while calling anonymous lambda
         at /home/matt/nixpkgs/ci/pkgs/stdenv/linux/default.nix:323:5:
          322|   (
          323|     prevStage:
             |     ^
          324|     # previous stage0 stdenv:

       … in the condition of the assert statement
         at /home/matt/nixpkgs/ci/pkgs/stdenv/linux/default.nix:325:5:
          324|     # previous stage0 stdenv:
          325|     assert isFromBootstrapFiles prevStage.binutils.bintools;
             |     ^
          326|     assert isFromBootstrapFiles prevStage."${localSystem.libc}";

       … from call site
         at /home/matt/nixpkgs/ci/pkgs/stdenv/linux/default.nix:325:12:
          324|     # previous stage0 stdenv:
          325|     assert isFromBootstrapFiles prevStage.binutils.bintools;
             |            ^
          326|     assert isFromBootstrapFiles prevStage."${localSystem.libc}";

       … while calling 'isFromBootstrapFiles'
         at /home/matt/nixpkgs/ci/pkgs/stdenv/linux/default.nix:130:26:
          129|   isFromNixpkgs = pkg: !(isFromBootstrapFiles pkg);
          130|   isFromBootstrapFiles = pkg: pkg.passthru.isFromBootstrapFiles or false;
             |                          ^
          131|   isBuiltByNixpkgsCompiler = pkg: isFromNixpkgs pkg && isFromNixpkgs pkg.stdenv.cc.cc;

       … from call site
         at /home/matt/nixpkgs/ci/pkgs/stdenv/booter.nix:74:20:
           73|             cur = op pred (builtins.elemAt list n) succ;
           74|             succ = go cur (n + 1);
             |                    ^
           75|           in

       … while calling 'go'
         at /home/matt/nixpkgs/ci/pkgs/stdenv/booter.nix:67:15:
           66|       go =
           67|         pred: n:
             |               ^
           68|         if n == len then

       … from call site
         at /home/matt/nixpkgs/ci/pkgs/stdenv/booter.nix:73:19:
           72|             # Note the cycle -- call-by-need ensures finite fold.
           73|             cur = op pred (builtins.elemAt list n) succ;
             |                   ^
           74|             succ = go cur (n + 1);

       … while calling 'folder'
         at /home/matt/nixpkgs/ci/pkgs/stdenv/booter.nix:96:26:
           95|   folder =
           96|     nextStage: stageFun: prevStage:
             |                          ^
           97|     let

       … from call site
         at /home/matt/nixpkgs/ci/pkgs/stdenv/booter.nix:110:11:
          109|         else
          110|           allPackages (
             |           ^
          111|             (removeAttrs args' [ "selfBuild" ])

       … while calling 'allPackages'
         at /home/matt/nixpkgs/ci/pkgs/top-level/default.nix:166:5:
          165|   allPackages =
          166|     newArgs:
             |     ^
          167|     import ./stage.nix (

       … from call site
         at /home/matt/nixpkgs/ci/pkgs/top-level/default.nix:167:5:
          166|     newArgs:
          167|     import ./stage.nix (
             |     ^
          168|       {

       … while calling anonymous lambda
         at /home/matt/nixpkgs/ci/pkgs/top-level/stage.nix:21:1:
           20|
           21| {
             | ^
           22|   ## Misc parameters kept the same for all stages

       … from call site
         at /home/matt/nixpkgs/ci/pkgs/top-level/stage.nix:374:1:
          373| # Check assertions and return the complete set of packages.
          374| handleAssertions (lib.fix toFix)
             | ^
          375|

       … while calling 'handleAssertions'
         at /home/matt/nixpkgs/ci/pkgs/top-level/stage.nix:371:5:
          370|   handleAssertions =
          371|     result: lib.asserts.checkAssertWarn (result.__assertions or [ ]) (result.__warnings or [ ]) result;
             |     ^
          372| in

       … from call site
         at /home/matt/nixpkgs/ci/pkgs/top-level/stage.nix:371:13:
          370|   handleAssertions =
          371|     result: lib.asserts.checkAssertWarn (result.__assertions or [ ]) (result.__warnings or [ ]) result;
             |             ^
          372| in

       … while calling 'checkAssertWarn'
         at /home/matt/nixpkgs/ci/lib/asserts.nix:193:27:
          192|   checkAssertWarn =
          193|     assertions: warnings: val:
             |                           ^
          194|     let

       … while calling the 'throw' builtin
         at /home/matt/nixpkgs/ci/lib/asserts.nix:198:7:
          197|     if failedAssertions != [ ] then
          198|       throw "\nFailed assertions:\n${concatStringsSep "\n" (map (x: "- ${x}") failedAssertions)}"
             |       ^
          199|     else

       … while calling the 'concatStringsSep' builtin
         at /home/matt/nixpkgs/ci/lib/asserts.nix:198:38:
          197|     if failedAssertions != [ ] then
          198|       throw "\nFailed assertions:\n${concatStringsSep "\n" (map (x: "- ${x}") failedAssertions)}"
             |                                      ^
          199|     else

       … while calling anonymous lambda
         at /home/matt/nixpkgs/ci/lib/asserts.nix:198:66:
          197|     if failedAssertions != [ ] then
          198|       throw "\nFailed assertions:\n${concatStringsSep "\n" (map (x: "- ${x}") failedAssertions)}"
             |                                                                  ^
          199|     else

       … from call site
         at /home/matt/nixpkgs/ci/lib/asserts.nix:198:74:
          197|     if failedAssertions != [ ] then
          198|       throw "\nFailed assertions:\n${concatStringsSep "\n" (map (x: "- ${x}") failedAssertions)}"
             |                                                                          ^
          199|     else

       … while calling anonymous lambda
         at /home/matt/nixpkgs/ci/lib/asserts.nix:195:31:
          194|     let
          195|       failedAssertions = map (x: x.message) (filter (x: !x.assertion) assertions);
             |                               ^
          196|     in

       … while evaluating the attribute 'message'
         at /home/matt/nixpkgs/ci/pkgs/top-level/stage.nix:197:11:
          196|           assertion = conflictingAttrs == { };
          197|           message = "The following attributes were defined both in `pkgs/top-level/all-packages.nix` and elsewhere, most likely in `pkgs/by-name/`: ${
             |           ^
          198|             lib.concatMapAttrsStringSep "\n" (

       … from call site
         at /home/matt/nixpkgs/ci/pkgs/top-level/stage.nix:198:13:
          197|           message = "The following attributes were defined both in `pkgs/top-level/all-packages.nix` and elsewhere, most likely in `pkgs/by-name/`: ${
          198|             lib.concatMapAttrsStringSep "\n" (
             |             ^
          199|               name: pkg: "- ${name}" + lib.optionalString (pkg ? meta.position) ": ${pkg.meta.position}"

       … while calling 'concatMapAttrsStringSep'
         at /home/matt/nixpkgs/ci/lib/strings.nix:334:13:
          333|   concatMapAttrsStringSep =
          334|     sep: f: attrs:
             |             ^
          335|     concatStringsSep sep (lib.attrValues (lib.mapAttrs f attrs));

       … while calling the 'concatStringsSep' builtin
         at /home/matt/nixpkgs/ci/lib/strings.nix:335:5:
          334|     sep: f: attrs:
          335|     concatStringsSep sep (lib.attrValues (lib.mapAttrs f attrs));
             |     ^
          336|

       … while calling anonymous lambda
         at /home/matt/nixpkgs/ci/pkgs/top-level/stage.nix:199:21:
          198|             lib.concatMapAttrsStringSep "\n" (
          199|               name: pkg: "- ${name}" + lib.optionalString (pkg ? meta.position) ": ${pkg.meta.position}"
             |                     ^
          200|             ) conflictingAttrs

       … from call site
         at /home/matt/nixpkgs/ci/pkgs/top-level/stage.nix:199:40:
          198|             lib.concatMapAttrsStringSep "\n" (
          199|               name: pkg: "- ${name}" + lib.optionalString (pkg ? meta.position) ": ${pkg.meta.position}"
             |                                        ^
          200|             ) conflictingAttrs

       … while calling 'optionalString'
         at /home/matt/nixpkgs/ci/lib/strings.nix:766:26:
          765|   */
          766|   optionalString = cond: string: if cond then string else "";
             |                          ^
          767|

       … while evaluating a branch condition
         at /home/matt/nixpkgs/ci/lib/strings.nix:766:34:
          765|   */
          766|   optionalString = cond: string: if cond then string else "";
             |                                  ^
          767|

       … from call site
         at /home/matt/nixpkgs/ci/pkgs/top-level/stage.nix:199:60:
          198|             lib.concatMapAttrsStringSep "\n" (
          199|               name: pkg: "- ${name}" + lib.optionalString (pkg ? meta.position) ": ${pkg.meta.position}"
             |                                                            ^
          200|             ) conflictingAttrs

       … while calling '_internalCallByNamePackageFile'
         at /home/matt/nixpkgs/ci/pkgs/top-level/by-name-overlay.nix:56:36:
           55|   # Because at that point the code in ./stage.nix can be changed to not allow definitions in `all-packages.nix` to override ones from `pkgs/by-name` anymore and throw an error if that happens instead.
           56|   _internalCallByNamePackageFile = file: self.callPackage file { };
             |                                    ^
           57| }

       … from call site
         at /home/matt/nixpkgs/ci/pkgs/top-level/by-name-overlay.nix:56:42:
           55|   # Because at that point the code in ./stage.nix can be changed to not allow definitions in `all-packages.nix` to override ones from `pkgs/by-name` anymore and throw an error if that happens instead.
           56|   _internalCallByNamePackageFile = file: self.callPackage file { };
             |                                          ^
           57| }

       … while calling 'callPackageWith'
         at /home/matt/nixpkgs/ci/lib/customisation.nix:260:19:
          259|   callPackageWith =
          260|     autoArgs: fn: args:
             |                   ^
          261|     let

       … while calling the 'abort' builtin
         at /home/matt/nixpkgs/ci/lib/customisation.nix:323:7:
          322|     else
          323|       abort "lib.customisation.callPackageWith: ${error}";
             |       ^
          324|

       error: evaluation aborted with the following error message: 'lib.customisation.callPackageWith: Function called without required argument "bazel_self" at /home/matt/nixpkgs/ci/pkgs/by-name/ba/bazel_7/package.nix:15'

This makes me think my current patch is still throwing too early.

EDIT: I tried moving the assertion furthher out to top-level/default.nix's checked function, but still see the same issue with callPackage args:

diff --git a/pkgs/top-level/default.nix b/pkgs/top-level/default.nix
index 12329d0af82f..3b829e896c0a 100644
--- a/pkgs/top-level/default.nix
+++ b/pkgs/top-level/default.nix
@@ -89,7 +89,8 @@ let
       lib.foldr
       (x: throwIfNot (lib.isFunction x) "All crossOverlays passed to nixpkgs must be functions.")
       (r: r)
-      crossOverlays;
+      crossOverlays
+      (lib.asserts.checkAssertWarn (pkgs.__assertions or [ ]) (pkgs.__warnings or [ ]));
 
   localSystem = lib.systems.elaborate args.localSystem;
 
diff --git a/pkgs/top-level/stage.nix b/pkgs/top-level/stage.nix
index 12d3b9315e9a..439c5e409c11 100644
--- a/pkgs/top-level/stage.nix
+++ b/pkgs/top-level/stage.nix
@@ -189,9 +189,19 @@ let
 
       conflictingAttrs = lib.intersectAttrs res super;
     in
-    assert lib.assertMsg (conflictingAttrs == { })
-      "The following attributes were defined both in `pkgs/top-level/all-packages.nix` and elsewhere, most likely in `pkgs/by-name/`: ${lib.concatStringsSep ", " (lib.attrNames conflictingAttrs)}";
-    res;
+    res
+    // {
+      __assertions = res.__assertions or [ ] ++ [
+        {
+          assertion = conflictingAttrs == { };
+          message = "The following attributes were defined both in `pkgs/top-level/all-packages.nix` and elsewhere, most likely in `pkgs/by-name/`: ${
+            lib.concatMapAttrsStringSep "\n" (
+              name: pkg: "- ${name}" + lib.optionalString (pkg ? meta.position) ": ${pkg.meta.position}"
+            ) conflictingAttrs
+          }";
+        }
+      ];
+    };
 
   aliases = self: super: lib.optionalAttrs config.allowAliases (import ./aliases.nix lib self super);
 

@wolfgangwalther
Copy link
Contributor Author

I tried to make this show the meta.position of the two cases, but ran into infinite recursion. Sad.

This is probably not even what would be useful. If we're dealing with by-name overrides in all-packages.nix, then both of these will point to the same package, that means their meta.position will be the same.

We might want to show the builtins.unsafeGetAttrPos of both definitions, aka the line in all-packages.nix and... exactly: the other one most likely doesn't have one, because it is in by-name.

I don't think it's worth spending cycles on trying to improve this more than what we have right now, tbh.

@MattSturgeon
Copy link
Contributor

We might want to show the builtins.unsafeGetAttrPos of both definitions, aka the line in all-packages.nix and... exactly: the other one most likely doesn't have one, because it is in by-name.

Showing the position of the definition in all-packages.nix is trivial:

diff --git a/pkgs/top-level/stage.nix b/pkgs/top-level/stage.nix
index 12d3b9315e9a..f9daeb4202f0 100644
--- a/pkgs/top-level/stage.nix
+++ b/pkgs/top-level/stage.nix
@@ -190,7 +190,18 @@ let
       conflictingAttrs = lib.intersectAttrs res super;
     in
     assert lib.assertMsg (conflictingAttrs == { })
-      "The following attributes were defined both in `pkgs/top-level/all-packages.nix` and elsewhere, most likely in `pkgs/by-name/`: ${lib.concatStringsSep ", " (lib.attrNames conflictingAttrs)}";
+      "The following attributes were defined both in `pkgs/top-level/all-packages.nix` and elsewhere, most likely in `pkgs/by-name/`: ${
+        lib.concatMapStrings (
+          name:
+          let
+            pos = builtins.unsafeGetAttrPos name res;
+          in
+          "\n- ${name}"
+          + lib.optionalString (
+            pos.file or null == toString ./all-packages.nix
+          ) " (line ${toString pos.line})"
+        ) (lib.attrNames conflictingAttrs)
+      }";
     res;
 
   aliases = self: super: lib.optionalAttrs config.allowAliases (import ./aliases.nix lib self super);

(The optionalString (pos.file or null == toString ./all-packages.nix) in that patch is just belts-and-braces and probably unnecessary; the position should always eval to a position in all-packages.nix if it's present in conflictingAttrs)

We can also manually look for an attr definition elsewhere, but you're right that in the typical by-name we won't find one:

diff --git a/pkgs/top-level/stage.nix b/pkgs/top-level/stage.nix
index f9daeb4202f0..a220ed14d997 100644
--- a/pkgs/top-level/stage.nix
+++ b/pkgs/top-level/stage.nix
@@ -194,12 +194,16 @@ let
         lib.concatMapStrings (
           name:
           let
+            prevPos = builtins.unsafeGetAttrPos name super;
             pos = builtins.unsafeGetAttrPos name res;
           in
           "\n- ${name}"
           + lib.optionalString (
             pos.file or null == toString ./all-packages.nix
           ) " (line ${toString pos.line})"
+          +
+            lib.optionalString (prevPos != null)
+              " also defined at ${lib.removePrefix "${toString ../../.}/" prevPos.file}:${toString prevPos.line}"
         ) (lib.attrNames conflictingAttrs)
       }";
     res;

Currently, this doesn't match anything.

One thing worth considering is the formatting. Should we put the line number at the start so that the list is more readable?

       error: The following attributes were defined both in `pkgs/top-level/all-packages.nix` and elsewhere, most likely in `pkgs/by-name/`:
       - aegisub (line 1001)
       - akkoma (line 986)
       - akkoma-admin-fe (line 997)
       - alsa-utils (line 9918)
       - aquamarine (line 2047)
       - armips (line 4428)
       - art (line 13207)
       - atf (line 8892)

vs

       error: The following attributes were defined both in `pkgs/top-level/all-packages.nix` and elsewhere, most likely in `pkgs/by-name/`:
       - L1001: aegisub
       - L986: akkoma
       - L997: akkoma-admin-fe
       - L9918: alsa-utils
       - L2047: aquamarine
       - L4428: armips
       - L13207: art

And then, should we consider sorting by line-number instead of by attr name?

@wolfgangwalther
Copy link
Contributor Author

Do we really need to print the line number at all? It should be trivial to look for foo = in all-packages.nix.

@MattSturgeon
Copy link
Contributor

MattSturgeon commented Oct 24, 2025

I don't think we need to, no.

It may be slightly helpful if someone has an editor that struggles searching through long files but is ok at jumping to a specific line, however I agree that's not really a strong justification for adding the additional code.

The only other scenario I can see it helping with, is a package with a very generic name that is hard to grep for. Again, kinda contrived.

Hopefully, once we've done the initial cleanup in #453948, people will only see the error when they add a new package to all-packages.nix, or duplicate a package elsewhere, so it will usually be obvious from their diff where the issue is.

That said, I usually default to putting as much info as practical into errors and warnings, so that they can be as useful as possible.

@wolfgangwalther
Copy link
Contributor Author

Not going to pursue this anymore myself (#469692).

@gepbird
Copy link
Contributor

gepbird commented Feb 2, 2026

This is continued in #483820.

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.

4 participants