cachix / devenv

Fast, Declarative, Reproducible, and Composable Developer Environments
https://devenv.sh
Apache License 2.0
4.04k stars 303 forks source link

Weird/broken state where flake-parts is missing from /nix/store #1360

Open therealpxc opened 1 month ago

therealpxc commented 1 month ago

Describe the bug Sometimes in projects created with the flake-parts template, one can run into a situation where the flake-parts checkout is missing from /nix/store (if it gets garbage collected).

Here's what it looks like with nix flake lock:

❯ nix build --no-eval-cache
error (ignored): error: end of string reached
error:
       … in the left operand of the update (//) operator
         at «nix-internal»/call-flake.nix:69:13:
           68|             # This is shadowed in the next //
           69|             // sourceInfo
             |             ^
           70|             // {

       … in the condition of the assert statement
         at «nix-internal»/call-flake.nix:79:13:
           78|           if node.flake or true then
           79|             assert builtins.isFunction flake.outputs;
             |             ^
           80|             result

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

       error: path '/nix/store/xvyy5vh6cg7958a26p2bqyz6jg5wkz4g-source' does not exist

Here's what it looks like from devenv via direnv:

❯ cd newproj
direnv: loading ~/Sandbox/newproj/.envrc
direnv: loading https://raw.githubusercontent.com/nix-community/nix-direnv/2.2.1/direnvrc (sha256-zelF0vLbEl5uaqrfIzbgNzJWGmLzCmYAkInj/LNxvKs=)
direnv: using flake . --override-input devenv-root file+file:///dev/fd/63
warning: not writing modified lock file of flake 'path:/Users/patcal04/Sandbox/newproj':
• Updated input 'devenv-root':
    'file:///dev/null?narHash=sha256-d6xi4mKdjkX2JFicDIv5niSzpyI0m/Hnm8GGAIU04kY%3D'
  → 'file:///dev/fd/63?narHash=sha256-5HWyQEBasqLgkrpefuCctVvx4vKd2An/cHWQHZOI4vQ%3D'
error (ignored): error: end of string reached
error:
       … in the left operand of the update (//) operator
         at «nix-internal»/call-flake.nix:69:13:
           68|             # This is shadowed in the next //
           69|             // sourceInfo
             |             ^
           70|             // {

       … in the condition of the assert statement
         at «nix-internal»/call-flake.nix:79:13:
           78|           if node.flake or true then
           79|             assert builtins.isFunction flake.outputs;
             |             ^
           80|             result

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

       error: path '/nix/store/xvyy5vh6cg7958a26p2bqyz6jg5wkz4g-source' does not exist
error (ignored): error: end of string reached
error:
       … while fetching the input 'path:/nix/store/xvyy5vh6cg7958a26p2bqyz6jg5wkz4g-source?lastModified=1712014858&narHash=sha256-sB4SWl2lX95bExY2gMFG5HIzvva5AVMJd4Igm%2BGpZNw%3D&rev=9126214d0a59633752a136528f5f3b9aa8565b7d'

       error: path '/nix/store/xvyy5vh6cg7958a26p2bqyz6jg5wkz4g-source' does not exist
direnv: nix-direnv: renewed cache

To reproduce Frankly, I'm not entirely sure how to reproduce. Every time I've hit this, it has caught me quite by surprise. I've hit it on Linux servers, a friend's WSL2 guest, and my MacBook.

I think it may require another bug to manifest. In particular, there is a bug (which I think is in upstream Nix, on Nix 2.23) where one can get lots of errors from ln during evaluation because there is no user-owned per-user gcroots directory in /nix/var/nix/gcroots. Incidentally, this is the place where gcroots for flake inputs end up.

I am trying to figure out steps to reproduce.

Make sure to include full logs and what you expected to happen.

Version

Paste the output of $ devenv version here or tell us if you're using flakes.

devenv 1.0.8 (aarch64-darwin)

Nix version:

nix (Nix) 2.23.3

Workaround

While I don't know how to reproduce, I have identified a workaround that works every time: add flake-parts to the inputs section of flake.nix explicitly. Deleting flake.lock also resolves the issue, so it seems the problem exists when flake.lock has flake-parts (or maybe anything with type indirect?) pointing to a non-existent store path.

I'll continue to work to find a way to reproduce this bug, and if the workaround is an acceptable fix, propose the same change to the flake-parts template with a pull request.

therealpxc commented 1 month ago

Another clue: I remembered that I have all the inputs of my system flake (the one containing this computer's darwinConfiguration) pinned in my system registry (and then again in my user registry, since Home Manager uses the same config). They end up getting pinned directly to file paths:

~/Sandbox/newproj 
❯ nix registry list | rg flake-parts
user   flake:flake-parts path:/nix/store/6n86v7gp9na15rfj5b6s7zv51qcjl58y-source?lastModified=1719994518&narHash=sha256-pQMhCCHyQGRzdfAkdJ4cIWiw%2BJNuWsTX7f0ZYSyz0VY%3D&rev=9227223f
system flake:flake-parts path:/nix/store/6n86v7gp9na15rfj5b6s7zv51qcjl58y-source?lastModified=1719994518&narHash=sha256-pQMhCCHyQGRzdfAkdJ4cIWiw%2BJNuWsTX7f0ZYSyz0VY%3D&rev=9227223f

So when I run nix flake lock and flake-parts is picked up implicitly from my local flake registry, it gets pinned directly to a store path which may or may not exist on some systems!

(I like this kind of pinning, especially for nixpkgs in the flake registry, because it means that I never have those stupid long download times when Nix randomly decides to update the cache of the registry, also when I run nix shell it shares things with my system stuff, etc.)

I still want to understand more about what's going on here, but I think it might be good to add flake-parts explicitly as an input to our flake-parts template so that it can't end up pointing directly to some other store path if that's what's in the local Nix registry. Seems like a good idea to insulate users from the vagaries of the flake registry.