// notes
nix-direnv: "minimum required bash version is 4.4"
nix-direnv fails with "minimum required bash version is 4.4" even though bash is newer. I break down the hard-coded /nix/store paths, the cause, and the fix
Context: rootless Nix, direnv, nix-direnv and a project with use flake. nix-direnv may refuse to build the devShell with a message about the minimum bash version even when bash is clearly newer. The cause is not bash: nix-direnv, built through resholve (a tool that replaces utility calls with absolute paths), keeps hard-coded paths under /nix/store, and outside the mount namespace (the isolated environment where /nix is mounted) those paths do not exist. Below: how to diagnose it and why the right fix is not "disable the check" but running direnv inside the namespace
Symptom
After editing .envrc or clearing the cache, the environment stops building:
direnv: using flake
direnv: nix-direnv: cache invalidated: profile (…/.direnv/flake-profile-…) does not exist
direnv: nix-direnv: minimum required bash version is 4.4 (installed: 5.3.15)
direnv: nix-direnv: Evaluating current devShell failed. Falling back to previous environment!bash 5.3.15 is installed, clearly above the required version. The dotenv part of .envrc still works: variables from .env are picked up. It is the flake build that fails
Why the message lies
The message about bash is a consequence, not the cause. The version check in nix-direnv works like this: the _require_version function compares the required and installed versions via printf ... | LC_ALL=C sort -c -V. If sort is not available at its hard-coded path, the pipe fails, the condition is treated as unmet, and a message is printed that is assembled at runtime:
minimum required $(basename "$cmd") version is $required (installed: $raw_version)resholve rewrites both utilities from this template to absolute store paths: sort in the condition and basename, which substitutes the command name. In one package they are embedded in the same coreutils directory, so their availability usually matches, but the text is assembled from two independent commands, and the resulting line depends on which paths resolve:
- if
basenameruns, the word is there:minimum required bash version is 4.4 (installed: 5.3.15), which is what the log at the top shows - if both paths are unavailable, the word is dropped:
minimum required version is 4.4 (installed: 5.3.15)with a double space, and stderr getsbasename: No such file or directory
Either way the cause is the same: unavailable hard-coded sort and basename paths, not an old bash. The bash check comes first, which is why the message mentions bash
The error points at the wrong cause
Cause: hard-coded paths in nix-direnv
nix-direnv 3.2.0 is built through resholve, a tool that replaces utility calls in scripts with absolute paths under /nix/store. As a result, direnvrc (a file from the package, share/nix-direnv/direnvrc, not a user config) has specific builds baked in:
/nix/store/<hash>-coreutils-9.11/bin/{basename,cat,chmod,mkdir,mktemp,mv,realpath,rm,sha1sum,sort,touch}
/nix/store/<hash>-nix-2.34.8/bin/nixFor Nix this is normal: the hash in the path pins an exact package version. The problem is different: these paths exist only inside the mount namespace that rootless Nix raises. Outside, there is no /nix directory, so there is neither the hard-coded sort and basename nor nix. sort brings down the version comparison, and basename cannot substitute the command name into the message, which is why the check fails
Fix
The right fix: run direnv entirely inside the namespace, then the hard-coded paths are valid and the check passes. A minimal wrapper example:
#!/usr/bin/env bash
# $HOME/.local/bin/nix-enter-direnv: run direnv inside the namespace
exec "$HOME/.local/bin/nix-enter" direnv "$@"Here nix-enter raises the namespace, and direnv runs inside it. The full namespace-aware nix wrapper is covered in "unshare failed from a nested namespace"
A safety net while diagnosing: the NIX_DIRENV_SKIP_VERSION_CHECK=1 variable disables version checks
export NIX_DIRENV_SKIP_VERSION_CHECK=1
direnv allowIf the environment builds with it and fails without it with the bash message, the version check is indeed the problem
Important: behavior depends on the installation source. In the nix-community build (default.nix), NIX_DIRENV_SKIP_VERSION_CHECK=1 is set automatically in the prologue, while the nixpkgs version does not do this. If your package comes from nixpkgs, the version check is active and the hard-coded paths matter
Verify
Find the hard-coded paths in direnvrc and confirm they do not resolve outside:
# which absolute paths are baked into nix-direnv
grep -o "/nix/store/[^ \"']*" "$HOME"/.nix/store/*-nix-direnv-*/share/nix-direnv/direnvrc | sort -u
# are we inside the namespace
test -d /nix/store && echo inside || echo outsideThe first grep shows which utilities are expected at absolute paths. The second test is the only reliable namespace marker. Do not rely on $NIX_STORE_DIR: it can be set from outside
Additionally: confirm that the garbage collector will not remove nix-direnv, because it lives in a profile (nix profile install)
nix-store --gc --print-dead | grep nix-direnvAn empty output means the package is not marked for removal
Pitfalls
Disabling the version check is not a fix: NIX_DIRENV_SKIP_VERSION_CHECK=1 hides the symptom but does not repair the hard-coded paths
Paths from the devShell silently do not work outside the namespace: the shell skips unavailable PATH entries and falls back to system binaries. More: "Nix devShell silently loses packages in fish"
Hashes in paths are not a secret and not a problem by themselves, but they tie the script to specific builds. Updating a package changes the hash and the path changes, so "this path works for me" does not prove it will work on someone else's machine
Takeaway
The minimum required bash version is 4.4 message does not mean an old bash; it means unresolved hard-coded paths in nix-direnv. The reliable fix is to run direnv inside the namespace. NIX_DIRENV_SKIP_VERSION_CHECK=1 is only good for confirming the diagnosis
Related notes
nix-direnv: "unshare failed: Operation not permitted"
direnv with use flake fails on unshare failed while dotenv works. I trace the cause (a nested user namespace) and show the fix for the nix wrapper
Nix devShell in fish: PATH is silently ignored
devShell packages are not picked up in fish because PATH points inside the mount namespace. How to diagnose the silent fallback and re-exec the shell
Who calls unshare: intercepting a binary through PATH
A command fails without naming the culprit. Here is the trick: replace the binary with a logging script via PATH and find the caller without tracing or root