garrytan / garrytan/gstack

browse: compiled binary's Bun.spawn PATH lookup fails on macOS even when bun is on PATH

Open
#931 2 comments 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
133k
Forks
19.9k
Avg merge
18h 46m
Merged PRs (30d)
26

Description

## 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:

1. Default shell PATH with `~/.bun/bin` first
2. Inline `PATH="$HOME/.bun/bin:$PATH"` override
3. Symlink `~/.bun/bin/bun` → `~/.local/bin/bun` (which IS in `$PATH`)
4. `launchctl setenv PATH "$HOME/.bun/bin:..."` (user-level launchd PATH)
5. `env -i HOME=$HOME PATH="$HOME/.bun/bin:..." browse status`
6. `BUN_INSTALL="$HOME/.bun" PATH="$HOME/.bun/bin:$PATH" browse status`
7. `cp ~/.bun/bin/bun /usr/local/bin/bun` (real file, root-owned, mode 755, in launchd-default PATH)
8. Downgrading bun from 1.3.11 to 1.3.10
9. Checking out gstack commit `1652f22` (older commit known to work elsewhere)
10. Clearing `com.apple.quarantine` xattr on browse + find-browse + bun
11. 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:

```diff
- 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.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.