=With programs.direnv.enableNushellIntegration = true, entering a directory that uses use flake .#X in its .envrc silently overwrites \$env.SHELL with the nix-store bash path, and never restores it after cd out. Reproducible on nushell ≥ 0.111.0.
Reproduction
.envrc
flake.nix
{ inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
outputs = { nixpkgs, ... }: {
devShells.aarch64-darwin.default =
nixpkgs.legacyPackages.aarch64-darwin.mkShell { };
};
}
Steps
direnv allow
echo $env.SHELL
cd /tmp
echo $env.SHELL
Root cause
Three independent design choices stack to produce a silent corruption:
- Nix's
mkShell exports a lowercase shell env var as part of stdenv's build environment.
- Direnv keeps
SHELL (uppercase) in its IgnoredKeys list but does not ignore lowercase shell. So direnv export json emits {"shell": "/nix/store/...-bash"} on load (and no SHELL).
- Nushell since PR #17558 (merged 2026-02-18, first shipped in 0.111.0) treats
\$env as case-insensitive on all platforms — so load-env { shell: ... } is the same as setting \$env.SHELL.
On unload, direnv emits {"shell": null}. The hook applies it; either the slot becomes empty or falls through to the OS-level $SHELL — but it's never restored to the pre-load value, because direnv never tracked `SHELL` to begin with.
Suggested fix
In `modules/programs/direnv.nix`, drop nix-mkShell-internal lowercase keys before `load-env`. Between `into record` and `load-env`:
| reject -i SHELL? shell? shellHook? builder? out? outputs? phases? stdenv? system? name?
`-i` makes the reject case-insensitive so it covers `shell` and `SHELL` regardless of how nushell or direnv evolve. The `?` suffix on each key makes it optional (no error if absent).
Workaround (until fixed)
Set `programs.direnv.enableNushellIntegration = false` and write a custom hook in `programs.nushell.configFile` / `extraConfig` with the `reject` line above.
References
=With
programs.direnv.enableNushellIntegration = true, entering a directory that usesuse flake .#Xin its.envrcsilently overwrites\$env.SHELLwith the nix-store bash path, and never restores it aftercdout. Reproducible on nushell ≥ 0.111.0.Reproduction
.envrcflake.nixSteps
Root cause
Three independent design choices stack to produce a silent corruption:
mkShellexports a lowercaseshellenv var as part of stdenv's build environment.SHELL(uppercase) in itsIgnoredKeyslist but does not ignore lowercaseshell. Sodirenv export jsonemits{"shell": "/nix/store/...-bash"}on load (and noSHELL).\$envas case-insensitive on all platforms — soload-env { shell: ... }is the same as setting\$env.SHELL.On unload, direnv emits
{"shell": null}. The hook applies it; either the slot becomes empty or falls through to the OS-level $SHELL — but it's never restored to the pre-load value, because direnv never tracked `SHELL` to begin with.Suggested fix
In `modules/programs/direnv.nix`, drop nix-mkShell-internal lowercase keys before `load-env`. Between `into record` and `load-env`:
`-i` makes the reject case-insensitive so it covers `shell` and `SHELL` regardless of how nushell or direnv evolve. The `?` suffix on each key makes it optional (no error if absent).
Workaround (until fixed)
Set `programs.direnv.enableNushellIntegration = false` and write a custom hook in `programs.nushell.configFile` / `extraConfig` with the `reject` line above.
References