Skip to content

Updating the nix input (e.g. #170) breaks runtime with "Symbolic link loop" #171

Description

@Hayao0819

The documented way to upgrade Nix in nix-portable is to override the nix flake input (see #27). With recent Nix versions (2.34, and likely any post-split-outputs release) this produces a binary that builds fine but fails at runtime:

error: unable to exec '~/.nix-portable/nix/store/q7f0d…-nix-2.34.6/bin/nix-store': Symbolic link loop

#170 is the most obvious trigger — it does exactly this input bump — but the underlying breakage is in the runtime, not the PR. Anyone following the #27 guidance on a modern Nix will hit it.

Reproduce

git fetch origin pull/170/head:pr-170
git checkout main && git merge pr-170
nix build .#nix-portable
rm -rf ~/.nix-portable
./result/bin/nix-portable nix --version

main alone works; main with the nix input bumped (or #170 merged) fails.

Cause

Modern Nix uses split outputs. bin/nix is an absolute symlink to another store path:

$ readlink /nix/store/q7f0d…-nix-2.34.6/bin/nix
/nix/store/3km45…-nix-2.34.6/bin/nix

The runtime extracts the embedded tarball with busybox tar (PATH is locked to \$dir/busybox/bin). busybox tar silently strips the leading / from symlink targets; GNU tar does not:

$ ln -sf /tmp/foo /tmp/symtest/link
$ tar cf t.tar -C /tmp symtest
$ busybox tar -xf t.tar -C /tmp/a && readlink /tmp/a/symtest/link
tmp/foo
$ tar       -xf t.tar -C /tmp/b && readlink /tmp/b/symtest/link
/tmp/foo

After extraction:

~/.nix-portable/nix/store/q7f0d…/bin/nix -> nix/store/3km45…/bin/nix   # relative, broken

Combined with the sibling nix-store -> nix, the kernel returns ELOOP.

The tarball itself is correct — the breakage is at extraction time:

$ zstd -dc storeTar/tar | tar -tvf - | grep 'bin/nix '
lrwxrwxrwx  nix/store/q7f0d…/bin/nix -> /nix/store/3km45…/bin/nix

The shipped 2.20.6 happens to dodge this because all its commands were sibling relative symlinks (`nix-build -> nix`) in one directory. Any Nix version that uses split outputs will trip it.

Impact

  • Upgrade nix version #170 cannot be merged as-is.
  • The flake-input-override upgrade path documented in How to upgrade nix? #27 is broken against current Nix.
  • Users who pin an older Nix won't see this, but newer pins (including upstream `master`) will.

Fix options

  1. Rewrite `nix/store/...` symlink targets to `/nix/store/...` after extraction.
  2. Use GNU static tar at runtime. `gnutar = pkgs.pkgsStatic.gnutar` is already an input but never installed into `$dir/bin`.
  3. Pin Nix to a pre-split-outputs version in the flake until the runtime is fixed.

Env

Arch Linux x86_64, kernel 7.0.5-zen.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions