#15475: fix(update): Handle Homebrew+Node Cellar path mismatch
stale
size: S
Cluster:
Update Handling Fixes
## Problem
On Homebrew-installed Node.js, there's a path mismatch that causes `gateway update.run` to install to the wrong location:
1. Binary at `/opt/homebrew/Cellar/node/X.Y.Z/bin/openclaw` is a symlink to `../lib/node_modules/openclaw/openclaw.mjs` (Cellar path)
2. But `npm root -g` returns `/opt/homebrew/lib/node_modules` (not Cellar)
3. So `npm i -g openclaw@latest` installs to the wrong location
4. The actual binary still points to the old version
**Symptoms:**
- `openclaw --version` shows old version after update
- User must manually run: `cd /opt/homebrew/Cellar/node/X.Y.Z/lib/node_modules && npm install openclaw@latest`
## Solution
1. **Detect Cellar paths** in `detectGlobalInstallManagerForRoot`:
- Check if pkgRoot matches the Homebrew Cellar pattern
- Return `npm` as manager even though paths don't match `npm root -g`
2. **Add `globalInstallArgsForCellar`**:
- Generate `npm install --prefix <cellar-lib> openclaw@latest` instead of `npm i -g`
- Targets the Cellar's node_modules directly
3. **Update runner uses Cellar args**:
- When pkgRoot is a Cellar path, use the Cellar install args
- Step name shows "(homebrew cellar)" for clarity
## Testing
- Existing tests pass (13 tests in update-runner.test.ts)
- Lint passes
- Manually verified the path detection logic matches the Homebrew structure
## Related
This issue is documented in our setup notes. The workaround was to manually install to the Cellar path. This PR automates that workaround.
<!-- greptile_comment -->
<h2>Greptile Overview</h2>
<h3>Greptile Summary</h3>
This PR adjusts global-update behavior to handle Homebrew Node “Cellar” installs where the `openclaw` binary symlink points into a versioned Cellar `lib/node_modules`, while `npm root -g` points to the non-Cellar prefix. It adds a Homebrew Cellar detection path in `detectGlobalInstallManagerForRoot`, introduces `globalInstallArgsForCellar` to install via `npm install --prefix <cellar-lib> ...`, and updates `runGatewayUpdate` to use those args and label the step accordingly.
<h3>Confidence Score: 3/5</h3>
- This PR is close to safe to merge, but has a real risk of selecting the wrong global manager on some systems.
- Most changes are localized and align with the described Homebrew Cellar mismatch, but the new Cellar fallback in `detectGlobalInstallManagerForRoot` can return `pnpm` based solely on the path shape. If pnpm is installed and `pnpm root -g` succeeds, updates can run the wrong command and fail to update the intended install.
- src/infra/update-global.ts
<sub>Last reviewed commit: 2c1ae34</sub>
<!-- greptile_other_comments_section -->
<!-- /greptile_comment -->
Most Similar PRs
#20584: fix: use stable Homebrew symlink for daemon node path
by mwfj · 2026-02-19
79.7%
#17815: fix: use $HOME as cwd for global update to prevent path-dedot panic
by frankekn · 2026-02-16
79.5%
#22406: Fix update detection for Companion App npm-prefix installs
by graysurf · 2026-02-21
79.5%
#12804: fix(daemon): use wrapper script for pnpm global installs in service...
by odinho · 2026-02-09
79.0%
#6064: fix(daemon): prefer bundled node from install-cli.sh over system node
by joyshmitz · 2026-02-01
78.3%
#18112: fix(daemon): gateway install on macOS ignores fnm/nvm node (#18090)
by yinghaosang · 2026-02-16
78.1%
#18961: fix: detect pnpm package manager in openclaw update
by norci · 2026-02-17
77.9%
#17237: fix(update): guard post-install imports after npm global update
by tdjackey · 2026-02-15
77.3%
#8600: fix(update): add --ignore-scripts to prevent supply chain attacks
by yubrew · 2026-02-04
77.2%
#19801: fix: pre-check write permissions before global install to prevent E...
by menhguin · 2026-02-18
76.7%