What version of Bun is running?
1.3.12+700fc117a
What platform is your computer?
Darwin 25.4.0 arm64 arm
What steps can reproduce the bug?
In Bun, execFileSync appears to resolve bare command names using a snapshot of the environment taken at process start. If process.env.PATH is mutated at runtime, Bun continues to resolve commands using the original PATH unless options.env is explicitly provided.
In Node.js, execFileSync honors runtime mutations to process.env.PATH by default, even when the options argument is omitted.
const { execFileSync } = require('node:child_process');
const fs = require('node:fs');
const path = require('node:path');
const os = require('node:os');
// 1. Create a "fake" binary in a temp directory
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'bun-path-test-'));
const fakeBinaryPath = path.join(tmpDir, 'ls');
fs.writeFileSync(fakeBinaryPath, '#!/bin/sh\necho "FAKE_CALLED"', { mode: 0o755 });
// 2. Prepend the temp directory to PATH at runtime
const originalPath = process.env.PATH;
process.env.PATH = `${tmpDir}${path.delimiter}${originalPath}`;
console.log(`Current process.env.PATH starts with: ${process.env.PATH.split(path.delimiter)[0]}`);
try {
// 3. Attempt to call 'ls' without an explicit env option
// Expected (Node.js behavior): Resolves to our fake binary and prints "FAKE_CALLED"
// Actual (Bun behavior): Resolves to /bin/ls and prints real directory listing
const output = execFileSync('ls', { encoding: 'utf8' }).split('\n')[0];
console.log(`Result (omitted env): ${output}`);
// 4. Attempt to call 'ls' with explicit env: process.env
// This works in both runtimes
const outputWithEnv = execFileSync('ls', { encoding: 'utf8', env: process.env }).split('\n')[0];
console.log(`Result (explicit env): ${outputWithEnv}`);
} finally {
// Cleanup
fs.rmSync(tmpDir, { recursive: true, force: true });
}
What is the expected behavior?
Current process.env.PATH starts with: /tmp/bun-path-test-XXXX
Result (omitted env): FAKE_CALLED
Result (explicit env): FAKE_CALLED
Both calls should resolve to the fake binary since process.env.PATH was updated before either call.
What do you see instead?
Current process.env.PATH starts with: /tmp/bun-path-test-XXXX
Result (omitted env): [Real file list from /bin/ls]
Result (explicit env): FAKE_CALLED
The call without explicit env resolves to the real /bin/ls instead of the fake binary prepended to PATH.
Additional information
- Workaround: Passing
env: process.env explicitly makes Bun behave like Node.js.
- Behavior difference: Node's default inherited environment reflects runtime
process.env mutations for command lookup. Bun appears to use a stale snapshot of the environment for the initial binary resolution unless a fresh environment object is passed.
- Scope: This has been observed with
execFileSync. Further testing may be required for spawn, exec, and other child_process variants.
What version of Bun is running?
1.3.12+700fc117a
What platform is your computer?
Darwin 25.4.0 arm64 arm
What steps can reproduce the bug?
In Bun,
execFileSyncappears to resolve bare command names using a snapshot of the environment taken at process start. Ifprocess.env.PATHis mutated at runtime, Bun continues to resolve commands using the original PATH unlessoptions.envis explicitly provided.In Node.js,
execFileSynchonors runtime mutations toprocess.env.PATHby default, even when theoptionsargument is omitted.What is the expected behavior?
Both calls should resolve to the fake binary since
process.env.PATHwas updated before either call.What do you see instead?
The call without explicit
envresolves to the real/bin/lsinstead of the fake binary prepended to PATH.Additional information
env: process.envexplicitly makes Bun behave like Node.js.process.envmutations for command lookup. Bun appears to use a stale snapshot of the environment for the initial binary resolution unless a fresh environment object is passed.execFileSync. Further testing may be required forspawn,exec, and otherchild_processvariants.