Fixing a broken cabal update

Struggling with build tools is maybe my least favorite part of programming, although working with encodings and environment variables is a close second. Today’s blog post involves all three!

Not really, though,The machine this was a problem on was running Ubuntu 18.04.5 LTS (“Bionic Beaver”). Oooh, someone needs to update their Ubuntu. That someone would be me. because Nix has relieved me from thinking about the environment variables.

Anyway, our story begins with an attempt to run cabal update. I was getting this error message:

$ cabal update
Downloading the latest package list from hackage.haskell.org
output of /usr/bin/wget: hGetContents: invalid argument (invalid byte sequence)

I checked my version of cabal-install; it was the latest version (3.2.0.0).

I searched for the error message. A couple of Stack Overflow posts and GitHub issues were about similar (but not exactly the same) issues and seemed helpful, but attempts to do as they suggested did not resolve the issue.

So we tried to figure out what to do. The parenthetical about an “invalid byte sequence” and some of the comments on those posts I read suggested it was probably due to an environmental variable on this machine, perhaps related to the encoding.

But because I am a Nix userI use NixOS on my laptops, but my desktop runs Ubuntu with the Nix package manager. It can be confusing since the word “Nix” often refers to different things., a Nix-based solution presented itself.

I could run the cabal update command within a Nix shell, and Nix will set the environment variables appropriately for the program and command it is running within the Nix shell where it is running the command, but without affecting the settings of any environment variables outside this context.

So, the solution to my problem was this command.

$ nix-shell --pure -p cabal-install --run "cabal update"

Let’s break that down:

  • The --pure flag clears the Nix shell of any environment variables that might be set in my shell. Normally, when you invoke nix-shell, if there is a shell.nix file in the current working directory, Nix builds the necessary dependencies and prepares the environment for those packages, inheriting some environment variables from the parent shell. Opening a Nix shell with the --pure flag clears the Nix shell of all inherited environment variables. It purifies the Nix environment, so to speak.

  • The -p flag is the short version of --packages and is followed by its argument. In this case the package we want in our Nix shell is cabal-install.

  • The --run flag runs the specified command (given in quotes following the flag) and then exits the shell. Usually a Nix shell is an interactive shell so you can open it and run different commands within the shell, but invoking the shell this way opens a noninteractive shell that just runs this command.

As I said, the Nix shell sets the environment variables within the shell appropriately for whatever packages it’s dealing with (and, again, this is without affecting their settings globally). You can look at the environments outside and inside the Nix shell, if you like, to see the changes.

For example, on this machine, if I run env in my normal Bash shell, I get some output that looks like this (although I’m leaving a lot out here for the sake of brevity).

$ env
CLUTTER_IM_MODULE=xim
LS_COLORS= < lotta letters and numbers in here >
LESSCLOSE=/usr/bin/lesspipe %s %s
XDG_MENU_PREFIX=gnome-
LANG=en_US.UTF-8
DISPLAY=:1
< ... and so on ... >

But if I run env in a Nix shell instead, it looks considerably different. For example, this is the env for a Nix shell with the cabal-install package.

$ nix-shell --pure -p cabal-install

[nix-shell:~/twitter]$ env
HOST_PATH= < a big path >
LS_COLORS= < lotta letters and numbers, again >
LESSCLOSE=/usr/bin/lesspipe %s %s
propagatedBuildInputs=
stdenv=/nix/store/333six1faw9bhccsx9qw5718k6b1wiq2-stdenv-linux
__ETC_PROFILE_SOURCED=1
DISPLAY=:1
out=/nix/store/k1h4df0jswivi8a9yfh9nds2vvssai7g-shell
CONFIG_SHELL=/nix/store/2jysm3dfsgby5sw5jgj43qjrb5v79ms9-bash-4.4-p23/bin/bash
buildInputs=/nix/store/1jpb9qg7ah9s2gj160y1vn7iwn0r6gy8-cabal-install-3.2.0.0
builder=/nix/store/2jysm3dfsgby5sw5jgj43qjrb5v79ms9-bash-4.4-p23/bin/bash
< ... and so on ... >

And if I grep for that LANG variable that showed up in my Bash shell environment, it’s no longer there in the Nix shell environment, which is configured specifically for this package and its needs.

Now, I don’t know that it’s the LANG variable that’s causing this problem with my cabal update but I do know that running the update command within this Nix shell fixed my problem.

Perhaps someday this post will help someone else figure out how to use a Nix shell to fix this problem or a similar problem or even write something new about their own problems. That’s the beauty of the internet.