Summary
On macOS Apple Silicon, ~/.claude/skills/gstack/browse/dist/browse fails on every invocation with:
[browse] Starting server...
[browse] Executable not found in $PATH: "bun"
…even though bun is installed and reachable from the shell. The bare Bun.spawn(['bun', 'run', SERVER_SCRIPT]) call at browse/src/cli.ts:243 cannot find bun from inside the compiled binary, but the same Bun.spawnSync(['bun', '--version']) call from the bun runtime in the same shell environment works fine. The bug is specific to bun build --compile'd binaries, not the bun runtime.
Environment
- macOS Apple Silicon (
arm64)
- bun 1.3.10 (also reproduced on 1.3.11)
- gstack: tested on
a7593d70 (HEAD when filed) and 1652f22 — same symptom on both
- bun installed via official installer at
~/.bun/bin/bun, also copied to /usr/local/bin/bun (root-owned, mode 755) which IS in the launchd-default PATH
- No Homebrew (
/opt/homebrew does not exist)
The isolating test
This is the key reproduction. With identical minimal env:
$ env -i HOME=$HOME PATH="$HOME/.bun/bin:/usr/local/bin:/usr/bin:/bin" \
~/.bun/bin/bun -e "
console.log('PATH inside bun:', process.env.PATH);
const r = Bun.spawnSync(['bun', '--version']);
console.log('bun spawn:', r.exitCode, r.stdout?.toString());
"
PATH inside bun: /Users/x/.bun/bin:/usr/local/bin:/usr/bin:/bin
bun spawn: 0 1.3.10
✅ Bun runtime: Bun.spawnSync(['bun', '--version']) succeeds, returns 1.3.10.
$ env -i HOME=$HOME PATH="$HOME/.bun/bin:/usr/local/bin:/usr/bin:/bin" \
~/.claude/skills/gstack/browse/dist/browse status
[browse] Starting server...
[browse] Executable not found in $PATH: "bun"
❌ Compiled browse binary: same Bun.spawn(['bun', ...]) fails to find bun with the exact same PATH.
So the issue is not "bun is not on PATH" — it's that the compiled binary's PATH-lookup primitive is broken in a way that the bun runtime's is not.
Things that don't fix it
Tried, none work:
- Default shell PATH with
~/.bun/bin first
- Inline
PATH="$HOME/.bun/bin:$PATH" override
- Symlink
~/.bun/bin/bun → ~/.local/bin/bun (which IS in $PATH)
launchctl setenv PATH "$HOME/.bun/bin:..." (user-level launchd PATH)
env -i HOME=$HOME PATH="$HOME/.bun/bin:..." browse status
BUN_INSTALL="$HOME/.bun" PATH="$HOME/.bun/bin:$PATH" browse status
cp ~/.bun/bin/bun /usr/local/bin/bun (real file, root-owned, mode 755, in launchd-default PATH)
- Downgrading bun from 1.3.11 to 1.3.10
- Checking out gstack commit
1652f22 (older commit known to work elsewhere)
- Clearing
com.apple.quarantine xattr on browse + find-browse + bun
- Full clean reinstall (
rm -rf ~/.claude/skills/gstack && git clone … && setup)
Side issue: build-node-server.sh fails on bun ≥ 1.3.10
During gstack/setup, this prints:
Building Node-compatible server bundle...
error: cannot write multiple output files without an output directory
…from browse/scripts/build-node-server.sh's bun build --target=node --outfile … call. Looks like --outfile semantics for --target=node changed in recent bun versions and now require --outdir when the build produces multiple chunks. Setup continues past the error and the fallback server-node.mjs is never written. On the gstack 1652f22 commit this error does NOT appear and server-node.mjs is built correctly, so something between 1652f22 and HEAD also pushed the build-script over the edge — but this is a separate issue from the spawn bug above.
Workaround that fixes it for me
Patched browse/src/cli.ts:243 to use an absolute path to bun resolved at runtime, skipping PATH lookup entirely:
- proc = Bun.spawn(['bun', 'run', SERVER_SCRIPT], {
+ // Use absolute path to bun. Bun.spawn's PATH lookup is broken in
+ // bun build --compile'd binaries on some macOS machines (see issue link).
+ const bunPath = (process.env.BUN_INSTALL ? `${process.env.BUN_INSTALL}/bin/bun` : null)
+ || (fs.existsSync('/usr/local/bin/bun') ? '/usr/local/bin/bun' : null)
+ || (process.env.HOME ? `${process.env.HOME}/.bun/bin/bun` : null)
+ || 'bun';
+ proc = Bun.spawn([bunPath, 'run', SERVER_SCRIPT], {
stdio: ['ignore', 'pipe', 'pipe'],
env: { ...process.env, BROWSE_STATE_FILE: config.stateFile, ...extraEnv },
});
proc.unref();
Recompile, retest:
$ ~/.claude/skills/gstack/browse/dist/browse status
[browse] Starting server...
Status: healthy
Mode: launched
URL: about:blank
Tabs: 1
PID: 21867
browse goto, browse snapshot, etc. all work end-to-end after this patch.
What might be the underlying bun bug
I am not equipped to fix this in bun itself, but the symptoms suggest:
bun build --compile produces a single-file executable
- Inside that executable,
Bun.spawn's PATH-lookup primitive (posix_spawnp / execvp / Bun's own which-equivalent) does not see process.env.PATH correctly, OR uses a hardcoded fallback that doesn't include ~/.bun/bin or /usr/local/bin
- This is specific to
--compile'd binaries because the same Bun.spawn(['bun', ...]) call from a non-compiled bun -e "…" script in the same env works
Could be a --compile flag baking in compile-time process.env instead of using runtime env, or a Bun.spawn code path that diverges between embedded vs runtime mode. I'd be happy to test additional repro variants if a Bun maintainer wants to chase it.
What I'd suggest gstack do regardless of the bun fix
The absolute-path workaround above is bulletproof — even on a working machine, it removes a moving part (PATH lookup) for no real cost. If BUN_INSTALL is set (the normal case for bun-installed-via-installer machines), use ${BUN_INSTALL}/bin/bun directly. If not, fall through to /usr/local/bin/bun, then ~/.bun/bin/bun, then bare 'bun' as the last resort. Same approach for find-browse and any other compiled binary that spawns bun by name.
Happy to send a PR if you'd like.
Summary
On macOS Apple Silicon,
~/.claude/skills/gstack/browse/dist/browsefails on every invocation with:…even though
bunis installed and reachable from the shell. The bareBun.spawn(['bun', 'run', SERVER_SCRIPT])call atbrowse/src/cli.ts:243cannot findbunfrom inside the compiled binary, but the sameBun.spawnSync(['bun', '--version'])call from the bun runtime in the same shell environment works fine. The bug is specific tobun build --compile'd binaries, not the bun runtime.Environment
arm64)a7593d70(HEAD when filed) and1652f22— same symptom on both~/.bun/bin/bun, also copied to/usr/local/bin/bun(root-owned, mode 755) which IS in the launchd-default PATH/opt/homebrewdoes not exist)The isolating test
This is the key reproduction. With identical minimal env:
✅ Bun runtime:
Bun.spawnSync(['bun', '--version'])succeeds, returns1.3.10.❌ Compiled browse binary: same
Bun.spawn(['bun', ...])fails to findbunwith the exact same PATH.So the issue is not "bun is not on PATH" — it's that the compiled binary's PATH-lookup primitive is broken in a way that the bun runtime's is not.
Things that don't fix it
Tried, none work:
~/.bun/binfirstPATH="$HOME/.bun/bin:$PATH"override~/.bun/bin/bun→~/.local/bin/bun(which IS in$PATH)launchctl setenv PATH "$HOME/.bun/bin:..."(user-level launchd PATH)env -i HOME=$HOME PATH="$HOME/.bun/bin:..." browse statusBUN_INSTALL="$HOME/.bun" PATH="$HOME/.bun/bin:$PATH" browse statuscp ~/.bun/bin/bun /usr/local/bin/bun(real file, root-owned, mode 755, in launchd-default PATH)1652f22(older commit known to work elsewhere)com.apple.quarantinexattr on browse + find-browse + bunrm -rf ~/.claude/skills/gstack && git clone … && setup)Side issue: build-node-server.sh fails on bun ≥ 1.3.10
During
gstack/setup, this prints:…from
browse/scripts/build-node-server.sh'sbun build --target=node --outfile …call. Looks like--outfilesemantics for--target=nodechanged in recent bun versions and now require--outdirwhen the build produces multiple chunks. Setup continues past the error and the fallbackserver-node.mjsis never written. On the gstack1652f22commit this error does NOT appear andserver-node.mjsis built correctly, so something between1652f22and HEAD also pushed the build-script over the edge — but this is a separate issue from the spawn bug above.Workaround that fixes it for me
Patched
browse/src/cli.ts:243to use an absolute path to bun resolved at runtime, skipping PATH lookup entirely:Recompile, retest:
browse goto,browse snapshot, etc. all work end-to-end after this patch.What might be the underlying bun bug
I am not equipped to fix this in bun itself, but the symptoms suggest:
bun build --compileproduces a single-file executableBun.spawn's PATH-lookup primitive (posix_spawnp/execvp/ Bun's ownwhich-equivalent) does not seeprocess.env.PATHcorrectly, OR uses a hardcoded fallback that doesn't include~/.bun/binor/usr/local/bin--compile'd binaries because the sameBun.spawn(['bun', ...])call from a non-compiledbun -e "…"script in the same env worksCould be a
--compileflag baking in compile-timeprocess.envinstead of using runtime env, or a Bun.spawn code path that diverges between embedded vs runtime mode. I'd be happy to test additional repro variants if a Bun maintainer wants to chase it.What I'd suggest gstack do regardless of the bun fix
The absolute-path workaround above is bulletproof — even on a working machine, it removes a moving part (PATH lookup) for no real cost. If
BUN_INSTALLis set (the normal case for bun-installed-via-installer machines), use${BUN_INSTALL}/bin/bundirectly. If not, fall through to/usr/local/bin/bun, then~/.bun/bin/bun, then bare'bun'as the last resort. Same approach forfind-browseand any other compiled binary that spawnsbunby name.Happy to send a PR if you'd like.