Skip to content

[Fix] nvm use: do not pile up PATH entries in nested sessions - #3959

Open
maliv2 wants to merge 1 commit into
nvm-sh:masterfrom
maliv2:fix-change-path-accumulation
Open

maliv2 wants to merge 1 commit into
nvm-sh:masterfrom
maliv2:fix-change-path-accumulation

Conversation

@maliv2

@maliv2 maliv2 commented Oct 14, 2026

Copy link
Copy Markdown

Fixes #3561.

Problem

nvm use keeps prepending to PATH instead of replacing the previously used version, so PATH grows on every switch and old versions are never removed. The PATH in #3561 shows it:

$NVM_DIR/versions/node/v20.19.0/bin:$NVM_DIR/versions/node/v22.14.0/bin:$NVM_DIR/versions/node/v22.14.0/bin:$NVM_DIR/versions/node/v20.19.0/bin:...:/usr/local/bin:...:/usr/bin:/bin:...:$NVM_DIR/versions/node/v18.20.6/bin:...

Reproduction:

. ./nvm.sh --no-use
P="/usr/bin:/usr/local/bin:$NVM_DIR/versions/node/v4.5.0/bin"
P="$(nvm_change_path "$P" /bin "$NVM_DIR/versions/node/v7.1.0")"
P="$(nvm_change_path "$P" /bin "$NVM_DIR/versions/node/v8.0.0")"
echo "$P"
# before: $NVM_DIR/versions/node/v8.0.0/bin:$NVM_DIR/versions/node/v7.1.0/bin:/usr/bin:/usr/local/bin:$NVM_DIR/versions/node/v4.5.0/bin
# after:  $NVM_DIR/versions/node/v8.0.0/bin:/usr/bin:/usr/local/bin:$NVM_DIR/versions/node/v4.5.0/bin

Root cause

Since #1830 (for #1652), nvm_change_path prepends instead of replacing when a system bin dir shadows an nvm path, which happens when macOS path_helper reorders the PATH of a nested session. The check ((^|:)(/usr(/local)?)?/bin:.*$NVM_DIR/...) matches a system dir followed by any nvm path. Once a stale nvm path is left after the system dirs (inherited by a nested shell, tmux, or an editor's terminal), the check is true on every call, even though the nvm path that is actually used already comes before the system dirs. So every nvm use prepends one more entry.

Fix

Only look at the part of PATH before the first nvm path (${1%%"${NVM_DIR}"/*}). If it contains a system bin dir, prepend as before, so the #1652 case is unchanged. Otherwise, replace the first nvm path in place, as in the normal case. This also turns the two grep pipelines in that branch into one.

Tests

  • Added a case to test/fast/Unit tests/nvm_change_path where an nvm path was already prepended ahead of the system dirs. It fails before this change and passes after. The existing NVM fails to load within nested shell #1652 cases are unchanged and still pass.
  • nvm_change_path, nvm_change_path special characters, and nvm_strip_path pass under sh, bash, dash, zsh, and ksh. An NVM_DIR containing glob characters (/tmp/a*b[c]) was also checked under bash, dash, zsh, and ksh.
  • Fast suite on Ubuntu (bash, zsh, dash): the only failures are the same ones the unmodified master has in that environment (running as root, no tty). The fix adds no new failures. npm run eclint passes, and shellcheck -s {bash,sh,dash,ksh} reports nothing for the changed function.

`nvm_change_path` prepends the new version (instead of replacing the
existing nvm path) when a system bin dir precedes an nvm path, to handle
macOS `path_helper` reordering the `PATH` of nested sessions (nvm-sh#1652).
However, the check matched a system dir followed by *any* nvm path. Once
a stale nvm path sits after the system dirs (e.g. inherited by a nested
shell, tmux, or an editor's terminal), the check stays true forever, so
every `nvm use` prepends yet another entry and the previously used
version is never removed from the `PATH`.

Only consider the first nvm path: if it is not preceded by a system bin
dir, replace it in place as usual.

Fixes nvm-sh#3561
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

previously used node version is not removed from the PATH variable

1 participant