Shell snapshot drops Nix `PATH` entries while preserving `__ETC_PROFILE_NIX_SOURCED=1`, causing commands to disappear in `bash -lc`

Open 💬 2 comments Opened Mar 28, 2026 by garhbod

What version of Codex CLI is running?

codex-cli 0.116.0

What subscription do you have?

Plus

Which model were you using?

gpt-5.4

What platform is your computer?

Linux 6.12.74+deb13+1-amd64 x86_64 unknown

What terminal emulator and version are you using (if applicable)?

_No response_

What issue are you seeing?

In Codex 0.116.0, commands installed via Nix are available to the parent Codex process, but unavailable to tool commands executed through bash -lc.

The shell snapshot appears to serialize an inconsistent environment:

  • it preserves NIX_PROFILES
  • it preserves __ETC_PROFILE_NIX_SOURCED=1
  • but it rewrites PATH to only system directories

Because of that, later shell startup does not restore Nix paths: /etc/profile.d/nix.sh runs, but its delegated script exits early because __ETC_PROFILE_NIX_SOURCED is already set.

What steps can reproduce the bug?

  1. Start Codex in a Nix-based shell environment where the parent process PATH includes Nix profile bins.
  2. Run a tool command through Codex such as:
command -v rg
  1. Observe that it fails.
  2. Inspect the generated shell snapshot and compare its PATH to the parent process environment.

What is the expected behavior?

Commands available in the parent Codex environment should remain available in tool shell commands.

At minimum, if the snapshot preserves __ETC_PROFILE_NIX_SOURCED=1, it should also preserve the corresponding Nix-modified PATH.

Actual

The parent process has the correct Nix PATH, but the generated shell snapshot contains a reduced PATH. As a result, commands installed in the Nix profile are not found from bash -lc.

Example symptom:

command -v rg
# exits non-zero

Additional information

Evidence

Parent sandbox process environment includes Nix paths:

PATH=/tmp/codex-arg0/...:/nix/store/...-ripgrep-15.1.0/bin:/home/<user>/.nix-profile/bin:/nix/var/nix/profiles/default/bin:/usr/local/bin:/usr/bin:/bin:...
NIX_PROFILES=/nix/var/nix/profiles/default /home/<user>/.nix-profile
__ETC_PROFILE_NIX_SOURCED=1
SHELL=/home/<user>/.nix-profile/bin/fish

Generated shell snapshot contains:

declare -x NIX_PROFILES="/nix/var/nix/profiles/default /home/<user>/.nix-profile"
declare -x PATH="/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games"
declare -x SHELL="/home/<user>/.nix-profile/bin/fish"
declare -x __ETC_PROFILE_NIX_SOURCED="1"

Then bash -x -lc true shows shell init loading Nix startup, but it short-circuits immediately:

+ . /etc/profile.d/nix.sh
++ . /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh
+++ '[' -n 1 ']'
+++ return

So the shell snapshot has already made the environment inconsistent before command execution begins.

Likely root cause

The shell snapshot generator is reconstructing or sanitizing PATH incorrectly while preserving related Nix environment variables verbatim.

Suggested fix

One of:

  1. Preserve the actual inherited PATH in the shell snapshot.
  2. Do not preserve __ETC_PROFILE_NIX_SOURCED unless the snapshot also preserves the matching Nix PATH.
  3. Recompute the environment in a way that keeps Nix init state internally consistent.

Workaround

A local shell workaround is to prepend the Nix profile bin explicitly in user startup files, but this looks like a Codex environment snapshot bug rather than a user shell configuration problem.

View original on GitHub ↗

2 Comments

garhbod · 5 months ago

Same issue with flox. Codex gets flox env vars like FLOX_ENV but doesn't have the updated PATH from flox activate

JaysonGeng · 4 months ago

I put together a fix for this on my fork:

Root cause I confirmed locally:

  • shell snapshot generation starts a login shell before the snapshot script runs
  • that login shell can rewrite PATH before the script gets control
  • Codex was then serializing that rewritten PATH alongside inherited activation markers like __ETC_PROFILE_NIX_SOURCED=1 (and similarly FLOX_ENV-style state), which leaves the snapshot internally inconsistent

The fix here is to preserve the actual inherited parent-process PATH, not the login-shell-rewritten one:

  • stash the parent PATH into an internal env var before spawning the snapshot shell
  • restore PATH from that internal value when serializing exports for bash / zsh / sh
  • exclude the internal helper env var from snapshot output
  • add a bash login-init regression test that simulates .bash_profile rewriting PATH while keeping __ETC_PROFILE_NIX_SOURCED=1

This should address the flox follow-up too, because the behavior is no longer Nix-specific; it preserves the parent session's command lookup path instead of trusting a fallback login shell's reconstruction of it.

Validation I ran:

  • just fmt
  • direct reproducer passed: when .bash_profile rewrites PATH to system dirs, the generated snapshot still preserves the inherited Nix-style PATH and keeps __ETC_PROFILE_NIX_SOURCED=1
  • direct regression check passed: invalid export filtering still excludes PWD, invalid names, and NEXTEST_BIN_EXE_*, and the internal helper env var does not leak into the snapshot
  • git diff --check

I also attempted:

  • cargo test -p codex-core shell_snapshot_tests::bash_snapshot_preserves_inherited_path_across_login_init -- --exact

I retried that 4 times with a 60s hard timeout, but on this machine each run stayed in codex-core compilation and never reached the test body before timing out, so I can't honestly claim a local cargo test pass yet.

Per the current contributing guidelines (docs/contributing.md says external contributions are by invitation only), I'm not opening an upstream PR uninvited. If you'd like this as a PR, I can open it immediately once invited.