shell: resolve commands on the shell environment's PATH, not the startup snapshot, and scope VAR=value prefixes to one command - #42049
Conversation
…ronment has none The lookup PATH for argv[0] now comes only from the shell environment: a PATH=... prefix assignment, then the exported environment, then the platform default for an environment without PATH (_PATH_DEFPATH on POSIX). The which builtin resolves through the same helper.
…l environment has none Matches node:child_process and libuv on Windows, which search the current process PATH and copy it into a child whose environment block lacks one. Document that .env() replaces the whole environment.
cmd_local_env was never cleared, so a prefix assignment reached the environment and the PATH lookup of every later command in the same shell environment. Clear it when a command starts.
|
Warning Review limit reached
On-demand reviews are free for the next 12 days. After that, they cost $0.25 per reviewed file. Or wait 30 seconds for your next included review. View limit detailsLimit details: You’ve used all 10 included reviews currently available. Review configuration: ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Essentials Run ID: 📒 Files selected for processing (10)
Comment |
|
Updated 3:42 PM PT - Sep 8th, 2026
✅ @robobun, your commit 1b1f6b8beb8265a8a3b3cb8c6d98a008aa1471dc passed in 🧪 To try this PR locally: bunx bun-pr 42049That installs a local version of the PR into your bun-42049 --bun |
|
Status
PR: #42049 |
…l-no-launch-path-fallback
… and export PATH in a package.json script
|
Note for a rebase: #43055 moves the |
Problem
$`tool`runs atoolfound only on the PATH Bun started with, afterdelete process.env.PATHor with an.env({...})object withoutPATH.node:child_process,Bun.spawn({ env })and the shell'swhichsay not found. On Windows (keyPath),export PATH=...and runtimeprocess.env.PATHedits never reached the lookup.SpawnArgs::defaultseeded the lookup PATH from the startup environment snapshot (src/runtime/shell/subproc.rs), andfill_envoverrode it only for a key spelled exactlyPATH.cmd_local_envwas also never cleared, so aVAR=value cmdprefix reached every later command.Fix
ShellExecEnv::command_path(): aPATH=... cmdprefix, then the exported environment (case-insensitive on Windows), else_PATH_DEFPATHon POSIX and the process's currentPATHon Windows.Cmd.rsandwhichcall it. A command clearscmd_local_envwhen it starts.Bun.spawn({ env })(Fix bug with PATH in Bun.spawn #16067),node:child_processand libuv apply this rule to an env withoutPATH, and bash scopes a prefix assignment to its command.PATH=""still searches nothing.$.env({ FOO })plus a command outside/usr/bin:/binnow fails withbun: command not found. Docs and types now say.env()replaces the environment. Theexport vartest expected the prefix leak and is updated.Background
$`...`builds a shell whoseexport_envcopiesprocess.envat call time, or the.env()object.cmd_local_envholdsVAR=value cmdprefixes._PATH_DEFPATH(/usr/bin:/bin) is whatexecvpsearches whenPATHis unset. libuv on Windows searches the parent's currentPATHinstead.Notes
Repro on Linux with 1.4.3-canary (
dis a temp dir with an executablew5891hi,PATH=$d:$PATH bun repro.mjs):Real node 26 for comparison: an env without
PATHfinds/usr/bintools and not the temp dir tool;PATH=""finds nothing. bash and dash set a defaultPATHwhen started without one.Windows x64 probe (canary b52d3e5 vs this branch),
process.envexposes the key asPath:On Windows
process.envwrites go throughSetEnvironmentVariableW, so the current processPATHreflects runtime edits and deletes. On POSIX they do not reach the native environment (#40846 tracksBun.which/Bun.spawnreading the startup snapshot), one more reason the POSIX arm does not consult the process environment at all.whichchanges with it:PATH=<dir> which toolnow searches<dir>(bash does the same), andwhichin an environment withoutPATHuses the same default as running the command.Prefix scope:
FOO=leak true; printenv FOOprintedleakandPATH=/nonexistent true; sh -c 'echo hi'failed withcommand not found: shon 1.4.3, becausecmd_local_envkept the prefix for the rest of the script (the Zig implementation did the same). bash, dash and POSIX scope the assignment to the one command.Cmdnow clearscmd_local_envwhen it starts, which covers the child environment, the argv[0] lookup andwhich. Thebunshell > variables > export vartest asserted that a later command still sawBAZ=1from an earlier prefix; its expectation is updated with a comment. The never-instantiatedDISABLE_PATH_LOOKUP_FOR_ARV0generic onfill_envand the dangling// PATH = "";note inParsedShellScript.rsgo away withSpawnArgs.path.Not addressed here, tracked separately: #32202 (a
VAR=value cmdprefix for a variable that is also exported produces two entries in the child's environment block).Self-review asks that this revision addresses: carry a
PATH=<dir> which tooltest (commands/which.test.ts), keep a prefix assignment from reaching later commands now thatwhichreadscmd_local_env, stop the docs examples from modelling the minimal-object pattern right below the new note, use the process's currentPATHon Windows instead of searching nothing, and state the relation to #40565 here and on that PR.Tests carried over from #40565 (Windows-only, in the
external command resolution on Windowsblock):.env()withPATHadded to an object that already hasPath(the{ ...process.env, PATH }pattern), aPATH=prefix overexport PATH(extended to check that the next command uses the exportedPATHagain), andexport PATHin a package.json script run bybun run. All three fail withbun: command not foundon 1.4.3-canary (d745f03) and pass with this branch on Windows x64. Thewhich falls back to the process PATHtest from #40565 is not carried over: it asserts the startup-snapshot fallback that this PR removes on POSIX, andexternal command resolution without a PATH ignores the launch PATHcovers the new rule.Suites run with the debug build. Linux x64:
test/js/bun/shell/bunshell.test.ts,test/js/bun/shell/commands/,bunshell-instance,bunshell-default,exec,test/js/bun/spawn/spawn-path.test.ts,test/cli/install/bun-run.test.ts(the only failures are twolspermission tests that fail as root on main too). Windows x64:bunshell.test.ts,commands/which.test.ts,commands/,bunshell-instance,bunshell-file,exec,test/cli/install/bun-run.test.ts(pre-existing: fivetilde_expansiontests that needHOME, onermtest that trips on a debug-only warning).no test proof · iteration 2 · platform-specific test(s) that do not run on this machine, deferring to CI, which covers all platforms: test/js/bun/shell/commands/which.test.ts, test/js/bun/shell/bunshell.test.ts