AI / Automation April 27, 2026

2026 OpenClaw PATH, Homebrew prefixes, and MCP server executable failures under launchd on a ProxyMac Mac mini

ProxyMac Engineering Team April 27, 2026 ~12 min read

Teams running OpenClaw on rented Mac mini M4 hosts in Hong Kong, Japan, Korea, Singapore, or the United States frequently paste logs where MCP tool servers print env: node: No such file or directory or uvx: command not found even though the same command succeeds inside Terminal.app. This article is a PATH contract between your login shell and launchd: we explain why brew --prefix differs on Apple Silicon, how to read a LaunchAgent plist without guessing, and a five-step verification ladder that pairs with MCP server setup, deployment troubleshooting, and install and deploy guides. Expect one wide matrix comparing symptoms, explicit EnvironmentVariables XML you can paste after review, and CTA buttons aimed at help plus OpenClaw topic hub—not generic marketing copy. After binaries resolve, missing API keys are a separate class of failure—see MCP environment variables under launchd.

The Terminal versus launchd mystery: two different universes

Interactive shells on macOS execute /etc/zprofile, ~/.zprofile, and ~/.zshrc, which often prepend eval "$(/opt/homebrew/bin/brew shellenv)". launchd agents inherit a conservative environment: PATH frequently defaults to /usr/bin:/bin:/usr/sbin:/sbin unless your plist extends it. OpenClaw gateways that spawn MCP servers therefore see a stripped universe where npx or pnpm shims simply do not exist, even though which npx in Terminal prints /opt/homebrew/bin/npx. The failure is not “OpenClaw lost MCP” — it is ENOENT from execve because the kernel cannot resolve the interpreter path.

  • Prove the split: log printenv PATH from a one-shot ssh mini 'launchctl print gui/…' session versus a login shell.
  • Prefer absolute paths for the outer MCP binary while you stabilize PATH for helper tools.
  • Track drift in Git using patterns from config versioning so upgrades do not silently rotate brew roots.
Never rely on GUI PATH for unattended agents. Screen Sharing sessions can inject different environment tables than headless LaunchAgents—treat VNC debugging as human context, not automation truth.

Apple Silicon versus Intel Homebrew prefix matrix (three columns, different from Wi-Fi article)

Hardware generationDefault brew prefixTypical symptom when missing from PATH
Apple Silicon M4 Mac mini/opt/homebrewnode not found while /opt/homebrew/bin/node -v prints v22.x
Intel Mac mini (legacy)/usr/localMCP JSON still points at /opt/homebrew/bin/uvx copied from a laptop
Mixed dev fleetBoth prefixes on diskAgents pick wrong shim order when PATH lists /usr/local/bin before /opt/homebrew/bin

Plist EnvironmentVariables pattern that survives reboots

Add an EnvironmentVariables dictionary to the LaunchAgent that owns OpenClaw or the MCP supervisor. Order matters: place Homebrew shims before system paths, then append language managers such as ~/.local/bin for uv toolchains. After editing, always launchctl bootout gui/$(id -u)/domain plist followed by kickstart; relying on launchctl unload alone is brittle on macOS 14+. Pair the change with health probes from monitoring guide so regressions page within 3 minutes when PATH snaps.

Example snippet (validate before production)

<key>EnvironmentVariables</key> <dict> <key>PATH</key> <string>/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string> </dict>

Security note: widening PATH is safer than disabling SIP or disabling Gatekeeper. Still avoid world-writable directories earlier in PATH—follow secrets hygiene when pointing at user-writable bins.

Five checks before you rewrite MCP configs from scratch

  1. Hash the binary: shasum -a 256 $(which uvx) in Terminal, then compare against the absolute path you will embed in MCP JSON.
  2. Dump launchd environment: use launchctl print user/$(id -u)/limit plus the agent domain to confirm PATH actually loaded.
  3. Simulate non-login: run env -i PATH=/usr/bin:/bin /opt/homebrew/bin/npx --version to see whether npx itself survives minimal PATH.
  4. Correlate JSONL: grep for ENOENT in rolling logs described in JSONL diagnostics.
  5. Document rollback: snapshot LaunchAgent plist per upgrade rollback guide before mass-deploying PATH edits to HK / JP / KR / SG / US fleets.

Node, uv, and shim layout: why version managers explode under launchd

Developers love fnm, mise, or asdf because they multiplex dozens of Node runtimes per repo, but those tools almost always mutate PATH inside ~/.zshrc—which launchd never reads. When your MCP JSON calls npx @scope/server, npx itself must exist on disk at an absolute path; only afterwards does it consult its own cache under ~/.npm. A common failure on fresh ProxyMac minis is copying JSON from a laptop where corepack enable placed pnpm on PATH via the interactive shell, while the server plist still points at /usr/bin/env node resolving to the system stub (v18 on some images) instead of the v22 toolchain your lockfile assumes. The mismatch surfaces as cryptic ERR_PNPM_UNSUPPORTED_ENGINE lines buried under OpenClaw retries, not as a clean “binary missing” banner.

uv follows the same story: uvx is typically installed into ~/.local/bin or ~/.cargo/bin after a curl script, directories that are absent from launchd PATH even when present in Terminal. Rather than sprinkling another dozen entries into PATH, many teams vendor a tiny wrapper script under /usr/local/bin/mcp-env.sh (owned root:wheel, not world-writable) that exports PATH once, then execs the real MCP entrypoint—auditors prefer one reviewed file over twenty plist edits. Keep the wrapper under Git with the same discipline you use for gateway restart snippets so on-call engineers can diff what changed between Tuesday and Thursday deploys.

RuntimeWhere humans usually install itWhat launchd sees without fixes
Node via Homebrew/opt/homebrew/bin/nodeOnly /usr/bin/env resolves; brew shim missing → ENOENT
uv toolchains~/.local/bin/uvxTilde never expands in plist strings unless you expand it yourself
Corepack-managed pnpmShim beside nodeWorks only if the same node directory precedes /usr/bin in PATH
Operational tip: bake a smoke LaunchAgent that runs /opt/homebrew/bin/node -e "console.log(process.version)" every 15 minutes and ships the stdout line into your JSONL pipeline; when brew upgrades rotate the binary, you will see the version string jump before MCP customers notice.

FAQ

Should I wrap everything in /bin/zsh -lc? It works but hides errors and complicates signal handling—prefer explicit PATH unless you need login functions.

Does Rosetta brew break MCP? Only when binaries are x86_64 but the agent expects arm64—match file $(which node) to the architecture OpenClaw was compiled for.

Where do stdio hangs still appear? PATH fixes exec; buffering fixes pipes—read stdio buffering if MCP stalls after the binary launches.

Why ProxyMac Mac mini is the right place to freeze PATH contracts

A dedicated Mac mini M4 gives you immutable metal per tenant: once /opt/homebrew is validated, it stays on NVMe beside your OpenClaw workspace without another engineer brew uninstalling shared Jenkins executables. Native arm64 binaries avoid Rosetta surprises, unified memory keeps parallel MCP workers from fighting NUMA, and five regions let you pin compliance-close replicas while keeping Git-tracked plist snippets identical. After PATH is boring again, scale concurrency using parallel agents guide, reserve nodes on the pricing page, and route humans through help center when GUI prompts require VNC.

Lock PATH once, run OpenClaw everywhere

HK / JP / KR / SG / US · Apple Silicon M4