The openclaw-mlx-tts voice helper pulls in the full mlx-swift Metal shader stack, which some beta Xcode toolchains (e.g. Xcode 27 / macOS 27 SDK) cannot compile: the metal compiler dies non-deterministically (a different .metal file each run, 'Could not read serialized diagnostics file'). The main app builds fine, so an unrelated dev/proof build should not be blocked by the helper. Add OPENCLAW_SKIP_MLX_TTS=1 (matching the sibling SKIP_TSC/SKIP_UI_BUILD toggles) to package the app without the voice helper, gating both the per-arch build and the bundle copy. Refuse the flag for release builds, which must ship the helper (notarization verifies it), so a skipped build can never become a silently incomplete release.
3.9 KiB
summary, read_when, title
| summary | read_when | title | |
|---|---|---|---|
| Setup guide for developers working on the OpenClaw macOS app |
|
macOS dev setup |
macOS developer setup
Build and run the OpenClaw macOS application from source.
Prerequisites
- Xcode 26.2+ (Swift 6.2 toolchain), on the latest macOS available in Software Update.
- Node.js 24.15+ & pnpm for the gateway, CLI, and packaging scripts. Node 22.22.3+ also works.
1. Install dependencies
pnpm install
2. Build and package the app
./scripts/package-mac-app.sh
Outputs dist/OpenClaw.app. Without an Apple Developer ID certificate, the
script falls back to ad-hoc signing.
Set OPENCLAW_SKIP_MLX_TTS=1 to package a dev/proof build without the local
MLX voice helper. This skips the openclaw-mlx-tts binary and its large
mlx-swift Metal shader stack, which some beta Xcode toolchains cannot compile.
The resulting app has no on-device MLX voice; it is rejected for release
builds, which must ship the helper.
For dev run modes, signing flags, and Team ID troubleshooting, see
apps/macos/README.md.
Fast dev loop from repo root: scripts/restart-mac.sh (add --no-sign for
ad-hoc signing; TCC permissions do not stick with --no-sign).
3. Install the CLI and Gateway
The packaged app embeds the canonical scripts/install-cli.sh installer. On a
fresh profile, choose This Mac during onboarding; the app installs the
matching user-space CLI and runtime before starting the Gateway wizard.
For manual development recovery, install the matching CLI yourself:
npm install -g openclaw@<version>
pnpm add -g openclaw@<version> and bun add -g openclaw@<version> also
work. Node remains the recommended runtime for the Gateway itself.
Troubleshooting
Build fails: toolchain or SDK mismatch
The macOS app build expects the latest macOS SDK and the Swift 6.2 toolchain (Xcode 26.2+).
xcodebuild -version
xcrun swift --version
If versions don't match, update macOS/Xcode and re-run the build.
Build fails: MLX voice helper Metal shaders
On a beta-only Xcode toolchain (for example Xcode 27 with the macOS 27 SDK),
only the openclaw-mlx-tts helper may fail while the main app builds fine. The
mlx-swift Metal compilation errors non-deterministically (a different .metal
file each run, Could not read serialized diagnostics file then a nonzero
metal exit), because the beta metal compiler and its separately downloaded
Metal Toolchain are still unstable. This is an upstream toolchain issue, not an
OpenClaw one.
If you do not need on-device MLX voice, skip the helper:
OPENCLAW_SKIP_MLX_TTS=1 ./scripts/package-mac-app.sh
Otherwise, install the Metal Toolchain
(xcodebuild -downloadComponent MetalToolchain) and build from a stable Xcode
release.
App crashes on permission grant
If the app crashes when you try to allow Speech Recognition or Microphone access, it may be a corrupted TCC cache or signature mismatch.
-
Reset TCC permissions for the debug bundle id:
tccutil reset All ai.openclaw.mac.debug -
If that fails, temporarily change
BUNDLE_IDinscripts/package-mac-app.shto force a clean slate from macOS.
Gateway "Starting..." indefinitely
Check whether a zombie process holds the port:
openclaw gateway status
openclaw gateway stop
# If you're not using a LaunchAgent (dev mode / manual runs), find the listener:
lsof -nP -iTCP:18789 -sTCP:LISTEN
If a manual run holds the port, stop it (Ctrl+C), or kill the PID found above as a last resort.