Tmux bash prompt breaks inside of flakes

Hi guys, I recently took the leap into Nix flakes by converting my /etc/nixos directory to use one, and I also turned my old shell.nix which I used for development into a flake.nix.

However, I’m having a breaking issue with Tmux, which I use regularly during development. It only happens when using flake.nix + nix develop; I don’t get the same issue using nix-shell.

Specifically what’s happening is that any escaped character in my PS1 prompt seems to break inside of Tmux, displaying the \ escape characters. This would be annoying if it were only cosmetic, however it seems that some of the escaped characters are forming command sequences, which leads to lots of inputs becoming corrupted at the command line: for example, I can’t use the cursor keys to move up and down in the history, it instead just generates junk keycodes.

Here’s an example of what’s happening:
Normal prompt :

~/test via ❄️  impure (nix-shell-env) 
❯

In tmux:

\[\]~/test\[\] via \[\]❄️  impure (nix-shell-env)\[\]
\[\]❯\[\]

I’ve done extensive testing and this happens with Starship (used in the example) as well as more basic PS1 prompts, including the nixOS default of
\n\[\033[$PROMPT_COLOR\][\u@\h:\w]\\$\[\033[0m\] . If I use a simple plaintext prompt with no escape sequences, it works fine, but I would prefer to not be limited to uncolored plaintext prompts.

Again, this never happened while using nix-shell. It seems to happen any time I use nix develop, even with a completely bare-bones flake.nix that has no additional packages or shell hooks.

echo $PS1 gives the same results inside and outside of tmux (and the flake).

I’m at a loss for how to debug or fix this. I’m on the verge of going back to nix-shell because of this single issue, which currently breaks my workflow. Any help would be greatly appreciated.

What terminal emulator are you using? What’s the value of $TERM var in tmux shell and outside of it?

I’m using foot. $TERM is ‘foot’ outside of tmux, inside it’s ‘screen’ by default. However, I’ve also tried changing this to ‘foot’, ‘screen-256color’, ‘xterm-256color’, and ‘tmux-direct’, using both the NixOS programs.tmux.terminal option and other methods, and none of them had any different effect.

Can you post the sequence of steps that leads to the escape sequences breaking?

I just tried this:

  1. (since I am using kitty) nix run nixpkgs#foot

  2. (in foot) echo $TERM to make sure it’s indeed foot

  3. nix run nixpkgs#tmux -- -f /dev/null (to make sure my standard tmux config file is not loading)

  4. cd $(mktemp -d)

  5. nix develop with the following flake

    {
      outputs =
        { nixpkgs, self }:
        let
          pkgs = import nixpkgs { system = "x86_64-linux"; };
        in
        {
          devShells.x86_64-linux.default = pkgs.mkShell {
            nativeBuildInputs = [ pkgs.hello ];
          };
        };
    }
    

It launches bash and starship renders just fine.

Also, I think I remember seeing something like this (especially the up/down in history) when using bash instead of bashInteractive in a docker container with ttyd. It also made the mouse wheel emit garbage into the terminal instead of scrolling.

It happens when I use nix develop first, and then run tmux within the flake shell. Again, I did this all the time with nix-shell with no issues.

If I run tmux first and then do nix develop, the prompt continues displaying fine.

I’ve also tested using Gnome Console and gotten the same results, FWIW.

hmm, okay, I was doing some fiddling around and I discovered that I get the same problem if I just do a nix shell after doing nix develop. So it seems that the issue is unrelated to tmux.

FYI the default has been changed - bash: Make interactive by default by infinisil · Pull Request #379368 · NixOS/nixpkgs · GitHub

Though not sure this will affect dev shells.

Since the default has been changed, I’d say just put pkgs.bash (or pkgs.bashInteractive if on an older commit) in packages and call it a day. EDIT: that workaround doesn’t currently work.

1 Like

Toyed around and landed on this workaround that appears to work. Instead of the nix shell, try

SHELL=$(nix build nixpkgs#bash^out --no-link --print-out-paths)/bin/bash nix shell

exporting SHELL may also fix your tmux issue, e.g.

  pkgs.mkShell {
    packages = [ ... ];
    shellHook = ''
      export SHELL=${lib.getExe pkgs.bash}
    '';
  }

On unstable nix (where there is no non-interactive bash package):
$SHELL is already getting set to the nix store path for bash, so re-setting that in the shell hook does nothing. What did work is putting export SHELL="/run/current-system/sw/bin/bash" , which is the normal value of $SHELL outside of the flake, in the shell hook.

Is there any footgun hidden in this workaround? I would assume that nix develop uses a non-interactive shell for a reason…

No, it’s set to the store path for non-interactive bash. pkgs.bash (on unstable) or pkgs.bashInteractive (on stable) is interactive bash.