Debug a failed derivation with `breakpointHook` and `cntr`

These instructions describe how to get an interactive shell at the point in a derivation when the derivation fails.

This works in a Linux single-user installation of Nix.

Add the breakpointHook package to the nativeBuildInputs of a mkDerivation.

nativeBuildInputs = [ breakpointHook ];

Install cntr

nix-env -i cntr

Run your Nix build command, and when it fails, the breakpointHook will output
a message like this:

build failed in installPhase with exit code 1
To attach install cntr and run the following command as root:

   cntr attach -t command cntr-/nix/store/6vwxqrwq5h1fd3nw4mc61wgk7rppn2qw-jupyterlab-extended

So we run that command as root. The root user doesn’t have a PATH to the
cntr command which we installed in our Nix profile, so give it the full path to our single-user-installed cntr.

sudo /home/$USER/.nix-profile/bin/cntr attach -t command cntr-/nix/store/6vwxqrwq5h1fd3nw4mc61wgk7rppn2qw-jupyterlab-extended

And now we’re in the sleeping Nix container. From here, run cntr exec to fully load the build environment.

/home/$USER/.nix-profile/bin/cntr exec

The $TMPDIR directory is where Nix has created the temporary directory for the build.

cd $TMPDIR

Now we are fully inside the context of the build, and any commands which we
enter in the shell will be as if we had entered that command on a line
in the phase (for example, buildPhase) of our derivation when the derivation failed.

References

2 Likes

alternatively, I use nix-shell to see if anything odd is happening:

[05:45:36] jon@jon-desktop /home/jon/projects/nixpkgs (master)
$ nix-shell default.nix -A cmake
these paths will be fetched (8.54 MiB download, 8.55 MiB unpacked):
...
[nix-shell:/home/jon/projects/nixpkgs]$ unpackPhase
unpacking source archive /nix/store/rr3zil97zd9jmvdpw8milynchz9zjff2-cmake-3.18.0.tar.gz
source root is cmake-3.18.0
setting SOURCE_DATE_EPOCH to timestamp 1594811960 of file cmake-3.18.0/Utilities/std/cm/vector
[nix-shell:/home/jon/projects/nixpkgs]$ cd $sourceRoot
[nix-shell:/home/jon/projects/nixpkgs]$ patchPhase
...
[nix-shell:/home/jon/projects/nixpkgs]$ configurePhase
...
[nix-shell:/home/jon/projects/nixpkgs]$ buildPhase
...

since I don’t do --pure, I’m able to use tree, ripgrep, and other tools to find what I’m looking for.

And, if I do need to create a patch, I can do git init && git add . after cd $sourceRoot. Then after writing the changes I want, I can just do git diff -- <file> > my-patch.patch to generate my patch

1 Like

Thanks for the howto! This approach has come in very handy for me in the past when debugging builds that worked great in nix-shell, but not in the build sandbox.

2 Likes