Each script is self-contained. Set $NIX to the PR's nix binary
(defaults to nix from PATH).
export NIX=/path/to/pr15711/nix
./bug1-storePath.sh
./bug2-nix-copy.shprim_storePath calls store->ensurePath() on a lazily-mounted flake
path that has not been copied to the store yet. ensurePath tries to
substitute, finds nothing, and errors.
Worked before because flakes were eagerly copied to the store.
Fix: call state.ensureLazyPathCopied(path2) before ensurePath() in
prim_storePath (src/libexpr/primops.cc).
error: path '/nix/store/...-source/' does not exist
Root cause: PosixSourceAccessor keeps a process-global lstat cache
keyed by absolute path (static Cache cache at
src/libutil/posix-source-accessor.cc:193).
In impure eval, rootFS is a union of the real filesystem and storeFS.
While reading the flake, the union accessor probes the posix layer at
/nix/store/<hash>-source, gets ENOENT, and caches that negative
result globally. Later ensureLazyPathCopied() materialises the path
into the store, but the stale negative entry stays. When
Store::narFromPath (the default impl used by UDSRemoteStore) creates
a fresh PosixSourceAccessor rooted at that store path,
cachedLstat(root) returns the stale nullopt and the dump fails.
Only triggers with --impure (pure eval uses storeFS directly, no
posix probe). Re-running succeeds because the path then exists from the
start.
Possible fixes:
- have
ensureLazyPathCopied()clear the relevant stat-cache entries viaPosixSourceAccessor::clearCache(...)/similar after copying, or - make the
storeFS-mounted paths shadow the posix layer so the posix probe is never made for mounted store paths, or - make
UDSRemoteStore::narFromPathgo through the daemon instead of reading the local FS (i.e. drop theStore::narFromPathoverride).
Backtrace (from a debugoptimized build):
#0 nix::SourceAccessor::lstat source-accessor.cc:79
#1 ... dumpPath lambda archive.cc:53
#2 nix::SourceAccessor::dumpPath archive.cc:99
#3 nix::Store::narFromPath store-api.cc:366
#4 copyPaths source lambda store-api.cc:1074
This likely affects anything that, in a single impure-eval process,
materialises a lazy flake path and then reads it back via a
PosixSourceAccessor (e.g. nix copy, possibly nix bundle, nix store make-content-addressed, etc.).
See behavior-changes.sh.
nix flake metadata.pathmay not exist on disk.nix-instantiate --eval(read-only by default) prints store paths that do not exist;--read-write-modematerialises them.path:flakerefs still copy eagerly; onlygit:/github:/etc. benefit from lazy copying.
Only asserts storePath.name() == path.name(), not full equality. If
the mounted accessor produced different content than what was hashed at
mount time, this would silently copy to the wrong store path and leave
the expected one invalid. Probably unreachable with immutable git
accessors, but the assertion should be assert(storePath == path).