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.


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.



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.