Cycle Detection is maybe broken in 2.94 #1064

Open
opened 2025-12-02 17:12:04 +00:00 by nikstur · 6 comments

Describe the bug

In 2.94, something about cyclic dependency detection seems to be broken.

If you add cyclic outputs to libnet, Lix fails with:

libnet> shrinking RPATHs of ELF executables and libraries in /nix/store/n5f3h5il36jmr9wcng2w67jna27k5yv6-libnet-1.3-dev
libnet> checking for references to /build/ in /nix/store/n5f3h5il36jmr9wcng2w67jna27k5yv6-libnet-1.3-dev...
libnet> patching script interpreter paths in /nix/store/n5f3h5il36jmr9wcng2w67jna27k5yv6-libnet-1.3-dev
libnet> stripping (with command strip and flags -S -p) in  /nix/store/n5f3h5il36jmr9wcng2w67jna27k5yv6-libnet-1.3-dev/lib
error: Unexpected exception on the Lix daemon; this is a bug in Lix.
       We would appreciate a report of the circumstances it happened in at https://git.lix.systems/lix-project/lix.
       nix::ForeignException: map::at
┏━ 1 Errors:
┃ error: Unexpected exception on the Lix daemon; this is a bug in Lix.
┃        We would appreciate a report of the circumstances it happened in at https://git.lix.systems/lix-project/lix.
┃        nix::ForeignException: map::at

Steps To Reproduce

Add this patch to Nixpkgs:

diff --git i/pkgs/by-name/li/libnet/package.nix w/pkgs/by-name/li/libnet/package.nix
index c8a6c712c1d6..3633ee1856c5 100644
--- i/pkgs/by-name/li/libnet/package.nix
+++ w/pkgs/by-name/li/libnet/package.nix
@@ -18,6 +18,11 @@ stdenv.mkDerivation rec {
     hash = "sha256-P3LaDMMNPyEnA8nO1Bm7H0mW/hVBr0cFdg+p2JmWcGI=";
   };

+  outputs = [
+    "out"
+    "dev"
+  ];
+
   nativeBuildInputs = [
     autoconf
     automake

Expected behavior

Lix should show me how this cycle happened.

nix --version output

nix (Lix, like Nix) 2.94.0
System type: x86_64-linux
Additional system types: i686-linux, x86_64-v1-linux, x86_64-v2-linux, x86_64-v3-linux, x86_64-v4-linux
Features: gc, signed-caches
System configuration file: /etc/nix/nix.conf
User configuration files: /home/niklas/.config/nix/nix.conf:/etc/xdg/nix/nix.conf:/home/niklas/.nix-profile/etc/xdg/nix/nix.conf:/nix/profile/etc/xdg/nix/nix.conf:/home/niklas/.local/state/nix/profile/etc/xdg/nix/nix.conf:/etc/profiles/per-user/niklas/etc/xdg/nix/nix.conf:/nix/var/nix/profiles/default/etc/xdg/nix/nix.conf:/run/current-system/sw/etc/xdg/nix/nix.conf
Store directory: /nix/store
State directory: /nix/var/nix
Data directory: /nix/store/gr2f1x406db7yqymv5s9bml47shnyj7k-lix-2.94.0/share

cc @nikstur

## Describe the bug In 2.94, something about cyclic dependency detection seems to be broken. If you add cyclic outputs to libnet, Lix fails with: ``` libnet> shrinking RPATHs of ELF executables and libraries in /nix/store/n5f3h5il36jmr9wcng2w67jna27k5yv6-libnet-1.3-dev libnet> checking for references to /build/ in /nix/store/n5f3h5il36jmr9wcng2w67jna27k5yv6-libnet-1.3-dev... libnet> patching script interpreter paths in /nix/store/n5f3h5il36jmr9wcng2w67jna27k5yv6-libnet-1.3-dev libnet> stripping (with command strip and flags -S -p) in /nix/store/n5f3h5il36jmr9wcng2w67jna27k5yv6-libnet-1.3-dev/lib error: Unexpected exception on the Lix daemon; this is a bug in Lix. We would appreciate a report of the circumstances it happened in at https://git.lix.systems/lix-project/lix. nix::ForeignException: map::at ┏━ 1 Errors: ┃ error: Unexpected exception on the Lix daemon; this is a bug in Lix. ┃ We would appreciate a report of the circumstances it happened in at https://git.lix.systems/lix-project/lix. ┃ nix::ForeignException: map::at ``` ## Steps To Reproduce Add this patch to Nixpkgs: ```diff diff --git i/pkgs/by-name/li/libnet/package.nix w/pkgs/by-name/li/libnet/package.nix index c8a6c712c1d6..3633ee1856c5 100644 --- i/pkgs/by-name/li/libnet/package.nix +++ w/pkgs/by-name/li/libnet/package.nix @@ -18,6 +18,11 @@ stdenv.mkDerivation rec { hash = "sha256-P3LaDMMNPyEnA8nO1Bm7H0mW/hVBr0cFdg+p2JmWcGI="; }; + outputs = [ + "out" + "dev" + ]; + nativeBuildInputs = [ autoconf automake ``` ## Expected behavior Lix should show me how this cycle happened. ## `nix --version` output ```console nix (Lix, like Nix) 2.94.0 System type: x86_64-linux Additional system types: i686-linux, x86_64-v1-linux, x86_64-v2-linux, x86_64-v3-linux, x86_64-v4-linux Features: gc, signed-caches System configuration file: /etc/nix/nix.conf User configuration files: /home/niklas/.config/nix/nix.conf:/etc/xdg/nix/nix.conf:/home/niklas/.nix-profile/etc/xdg/nix/nix.conf:/nix/profile/etc/xdg/nix/nix.conf:/home/niklas/.local/state/nix/profile/etc/xdg/nix/nix.conf:/etc/profiles/per-user/niklas/etc/xdg/nix/nix.conf:/nix/var/nix/profiles/default/etc/xdg/nix/nix.conf:/run/current-system/sw/etc/xdg/nix/nix.conf Store directory: /nix/store State directory: /nix/var/nix Data directory: /nix/store/gr2f1x406db7yqymv5s9bml47shnyj7k-lix-2.94.0/share ``` cc @nikstur
Owner

Reproduced on fd38f625e with nix build --impure -E 'with import <nixpkgs> { }; libnet.overrideAttrs { outputs = [ "out" "dev" ]; }' --no-use-cgroups

Daemon's stack trace:

Exception: std::__exception_ptr::exception_ptr: map::at
Stack trace:
 0# nix::getStackTrace[abi:cxx11]() in /nix/store/vr4a39d4bw01793jl4qap839rlvmc143-lix-2.94.0/lib/liblixutil.so
 1# nix::logException(std::basic_string_view<char, std::char_traits<char> >, std::exception const&) in /nix/store/vr4a39d4bw01793jl4qap839rlvmc143-lix-2.94.0/lib/liblixutil.so
 2# 0x00007FFFF7F8CA84 in /nix/store/vr4a39d4bw01793jl4qap839rlvmc143-lix-2.94.0/lib/liblixmain.so
 3# 0x00007FFFF60BF1AA in /nix/store/dj06r96j515npcqi9d8af1d1c60bx2vn-gcc-14.3.0-lib/lib/libstdc++.so.6
 4# std::unexpected() in /nix/store/dj06r96j515npcqi9d8af1d1c60bx2vn-gcc-14.3.0-lib/lib/libstdc++.so.6
 5# nix::daemon::processConnection(nix::AsyncIoRoot&, nix::ref<nix::Store>, nix::FdSource&, nix::FdSink&, nix::TrustedFlag) in /nix/store/vr4a39d4bw01793jl4qap839rlvmc143-lix-2.94.0/lib/liblixstore.so
 6# 0x0000555555662C94 in nix-daemon
 7# 0x0000555555673DDB in nix-daemon
 8# 0x000055555571BE3C in nix-daemon
 9# 0x00005555557201FB in nix-daemon
10# nix::handleExceptions(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::function<int ()>) in /nix/store/vr4a39d4bw01793jl4qap839rlvmc143-lix-2.94.0/lib/liblixmain.so
11# 0x000055555571FAAB in nix-daemon
12# 0x00007FFFF5C2A47E in /nix/store/g8zyryr9cr6540xsyg4avqkwgxpnwj2a-glibc-2.40-66/lib/libc.so.6
13# __libc_start_main in /nix/store/g8zyryr9cr6540xsyg4avqkwgxpnwj2a-glibc-2.40-66/lib/libc.so.6
14# 0x00005555556377F5 in nix-daemon

Async task trace (probably incomplete):
#0: kj::Promise<Result<void>> nix::printNode(Node &, const std::string &, const std::string &, bool, bool, Store &, const StorePath &, const StorePath &, std::map<StorePath, Node> &, Strings &, ref<FSAccessor>) (lix/libstore/path-tree.cc:237:10)
#1: kj::Promise<Result<std::string>> nix::genGraphString(const StorePath &, const StorePath &, const std::map<StorePath, StorePathSet> &, Store &, bool, bool, std::optional<ref<FSAccessor>>) (lix/libstore/path-tree.cc:321:10)
#2: auto nix::LocalDerivationGoal::registerOutputs()::(anonymous class)::operator()(Cycle<std::string> &) const (lix/libstore/build/local-derivation-goal.cc:1892:6)
	building of '/nix/store/5ix3v8p27vfpv2j87q92wj5295ai0ha1-libnet-1.3.drv^out' from .drv file
#3: virtual kj::Promise<Result<SingleDrvOutputs>> nix::LocalDerivationGoal::registerOutputs() (lix/libstore/build/local-derivation-goal.cc:1892:6)
	building of '/nix/store/5ix3v8p27vfpv2j87q92wj5295ai0ha1-libnet-1.3.drv^out' from .drv file
#4: kj::Promise<Result<Goal::WorkResult>> nix::DerivationGoal::buildDone(std::shared_ptr<Error>) (lix/libstore/build/derivation-goal.cc:988:56)
	building of '/nix/store/5ix3v8p27vfpv2j87q92wj5295ai0ha1-libnet-1.3.drv^out' from .drv file
#5: kj::Promise<Result<Goal::WorkResult>> nix::LocalDerivationGoal::handleRawChild() (lix/libstore/build/local-derivation-goal.cc:2677:36)
	building of '/nix/store/5ix3v8p27vfpv2j87q92wj5295ai0ha1-libnet-1.3.drv^out' from .drv file
#6: virtual kj::Promise<Result<Goal::WorkResult>> nix::LocalDerivationGoal::tryLocalBuild() (lix/libstore/build/local-derivation-goal.cc:284:63)
	building of '/nix/store/5ix3v8p27vfpv2j87q92wj5295ai0ha1-libnet-1.3.drv^out' from .drv file
#7: kj::Promise<Result<Goal::WorkResult>> nix::DerivationGoal::outputsSubstitutionTried() (lix/libstore/build/derivation-goal.cc:361:47)
	building of '/nix/store/5ix3v8p27vfpv2j87q92wj5295ai0ha1-libnet-1.3.drv^out' from .drv file
#8: kj::Promise<Result<Goal::WorkResult>> nix::DerivationGoal::loadDerivation() (lix/libstore/build/derivation-goal.cc:222:41)
	building of '/nix/store/5ix3v8p27vfpv2j87q92wj5295ai0ha1-libnet-1.3.drv^out' from .drv file
#9: virtual kj::Promise<Result<std::vector<KeyedBuildResult>>> nix::Store::buildPathsWithResults(const std::vector<DerivedPath> &, BuildMode, std::shared_ptr<Store>) (lix/libstore/build/entry-points.cc:62:11)
#10: void nix::daemon::performOp(AsyncIoRoot &, TunnelLogger *, ref<Store>, TrustedFlag, WorkerProto::Version, Source &, BufferedSink &, WorkerProto::Op) (lix/libstore/daemon.cc:492:24)
Reproduced on fd38f625e with `nix build --impure -E 'with import <nixpkgs> { }; libnet.overrideAttrs { outputs = [ "out" "dev" ]; }' --no-use-cgroups` Daemon's stack trace: ``` Exception: std::__exception_ptr::exception_ptr: map::at Stack trace: 0# nix::getStackTrace[abi:cxx11]() in /nix/store/vr4a39d4bw01793jl4qap839rlvmc143-lix-2.94.0/lib/liblixutil.so 1# nix::logException(std::basic_string_view<char, std::char_traits<char> >, std::exception const&) in /nix/store/vr4a39d4bw01793jl4qap839rlvmc143-lix-2.94.0/lib/liblixutil.so 2# 0x00007FFFF7F8CA84 in /nix/store/vr4a39d4bw01793jl4qap839rlvmc143-lix-2.94.0/lib/liblixmain.so 3# 0x00007FFFF60BF1AA in /nix/store/dj06r96j515npcqi9d8af1d1c60bx2vn-gcc-14.3.0-lib/lib/libstdc++.so.6 4# std::unexpected() in /nix/store/dj06r96j515npcqi9d8af1d1c60bx2vn-gcc-14.3.0-lib/lib/libstdc++.so.6 5# nix::daemon::processConnection(nix::AsyncIoRoot&, nix::ref<nix::Store>, nix::FdSource&, nix::FdSink&, nix::TrustedFlag) in /nix/store/vr4a39d4bw01793jl4qap839rlvmc143-lix-2.94.0/lib/liblixstore.so 6# 0x0000555555662C94 in nix-daemon 7# 0x0000555555673DDB in nix-daemon 8# 0x000055555571BE3C in nix-daemon 9# 0x00005555557201FB in nix-daemon 10# nix::handleExceptions(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::function<int ()>) in /nix/store/vr4a39d4bw01793jl4qap839rlvmc143-lix-2.94.0/lib/liblixmain.so 11# 0x000055555571FAAB in nix-daemon 12# 0x00007FFFF5C2A47E in /nix/store/g8zyryr9cr6540xsyg4avqkwgxpnwj2a-glibc-2.40-66/lib/libc.so.6 13# __libc_start_main in /nix/store/g8zyryr9cr6540xsyg4avqkwgxpnwj2a-glibc-2.40-66/lib/libc.so.6 14# 0x00005555556377F5 in nix-daemon Async task trace (probably incomplete): #0: kj::Promise<Result<void>> nix::printNode(Node &, const std::string &, const std::string &, bool, bool, Store &, const StorePath &, const StorePath &, std::map<StorePath, Node> &, Strings &, ref<FSAccessor>) (lix/libstore/path-tree.cc:237:10) #1: kj::Promise<Result<std::string>> nix::genGraphString(const StorePath &, const StorePath &, const std::map<StorePath, StorePathSet> &, Store &, bool, bool, std::optional<ref<FSAccessor>>) (lix/libstore/path-tree.cc:321:10) #2: auto nix::LocalDerivationGoal::registerOutputs()::(anonymous class)::operator()(Cycle<std::string> &) const (lix/libstore/build/local-derivation-goal.cc:1892:6) building of '/nix/store/5ix3v8p27vfpv2j87q92wj5295ai0ha1-libnet-1.3.drv^out' from .drv file #3: virtual kj::Promise<Result<SingleDrvOutputs>> nix::LocalDerivationGoal::registerOutputs() (lix/libstore/build/local-derivation-goal.cc:1892:6) building of '/nix/store/5ix3v8p27vfpv2j87q92wj5295ai0ha1-libnet-1.3.drv^out' from .drv file #4: kj::Promise<Result<Goal::WorkResult>> nix::DerivationGoal::buildDone(std::shared_ptr<Error>) (lix/libstore/build/derivation-goal.cc:988:56) building of '/nix/store/5ix3v8p27vfpv2j87q92wj5295ai0ha1-libnet-1.3.drv^out' from .drv file #5: kj::Promise<Result<Goal::WorkResult>> nix::LocalDerivationGoal::handleRawChild() (lix/libstore/build/local-derivation-goal.cc:2677:36) building of '/nix/store/5ix3v8p27vfpv2j87q92wj5295ai0ha1-libnet-1.3.drv^out' from .drv file #6: virtual kj::Promise<Result<Goal::WorkResult>> nix::LocalDerivationGoal::tryLocalBuild() (lix/libstore/build/local-derivation-goal.cc:284:63) building of '/nix/store/5ix3v8p27vfpv2j87q92wj5295ai0ha1-libnet-1.3.drv^out' from .drv file #7: kj::Promise<Result<Goal::WorkResult>> nix::DerivationGoal::outputsSubstitutionTried() (lix/libstore/build/derivation-goal.cc:361:47) building of '/nix/store/5ix3v8p27vfpv2j87q92wj5295ai0ha1-libnet-1.3.drv^out' from .drv file #8: kj::Promise<Result<Goal::WorkResult>> nix::DerivationGoal::loadDerivation() (lix/libstore/build/derivation-goal.cc:222:41) building of '/nix/store/5ix3v8p27vfpv2j87q92wj5295ai0ha1-libnet-1.3.drv^out' from .drv file #9: virtual kj::Promise<Result<std::vector<KeyedBuildResult>>> nix::Store::buildPathsWithResults(const std::vector<DerivedPath> &, BuildMode, std::shared_ptr<Store>) (lix/libstore/build/entry-points.cc:62:11) #10: void nix::daemon::performOp(AsyncIoRoot &, TunnelLogger *, ref<Store>, TrustedFlag, WorkerProto::Version, Source &, BufferedSink &, WorkerProto::Op) (lix/libstore/daemon.cc:492:24) ```
Owner

This doesn't fully happen before e2641cb890 (cc @ma27), but on that commit the daemon doesn't fully crash, merely hanging up early (and no stack trace).

This doesn't *fully* happen before e2641cb8907fe6344ec4d220c4764187dfb25e3f (cc @ma27), but *on* that commit the daemon doesn't fully crash, merely hanging up early (and no stack trace).
Owner

Ah, no I think our crash handler is just being unreliable. So: bisected to e2641cb890, cc @ma27.

Ah, no I think our crash handler is just being unreliable. So: bisected to e2641cb8907fe6344ec4d220c4764187dfb25e3f, cc @ma27.
Member

I'm aware and looking.

I'm aware and looking.
ma27 self-assigned this 2025-12-02 18:45:41 +00:00
Member

OK, the culprit was a very dumb oversight of mine, I didn't notice that this will break once an output not only has references to other outputs, but also to paths from a different derivation (and this only got triggered on this error, so the blast-radius is luckily rather low).

I'm very sorry for this.
Expect a patch today.

EDIT: I think we should ship this in 2.94.1. Feel free to remove again if you disagree.

OK, the culprit was a very dumb oversight of mine, I didn't notice that this will break once an output not only has references to other outputs, but also to paths from a different derivation (and this only got triggered on this error, so the blast-radius is luckily rather low). I'm very sorry for this. Expect a patch today. EDIT: I think we should ship this in 2.94.1. Feel free to remove again if you disagree.
ma27 added this to the 2.94.1 milestone 2025-12-05 10:09:16 +00:00
Member

This issue was mentioned on Gerrit on the following CLs:

  • commit message in cl/4701 ("libstore: fix reporting output cycles on drvs with references to other drvs")
<!-- GERRIT_LINKBOT: {"cls": [{"backlink": "https://gerrit.lix.systems/c/lix/+/4701", "number": 4701, "kind": "commit message"}], "cl_meta": {"4701": {"change_title": "libstore: fix reporting output cycles on drvs with references to other drvs"}}} --> This issue was mentioned on Gerrit on the following CLs: * commit message in [cl/4701](https://gerrit.lix.systems/c/lix/+/4701) ("libstore: fix reporting output cycles on drvs with references to other drvs")
Sign in to join this conversation.
No milestone
No project
No assignees
4 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
lix-project/lix#1064
No description provided.