#22313: fix: check plist existence before marking LaunchAgent missing
gateway
cli
size: S
trusted-contributor
Cluster:
Gateway and macOS Improvements
## Summary
When `launchctl print` fails, the current code unconditionally sets `missingUnit: true`. This is incorrect when the LaunchAgent plist file exists but the service simply isn't loaded.
Additionally, this PR now includes a complete `start()` implementation for all daemon backends, allowing OpenClaw to explicitly start services that are installed but not running.
## Problem
The `readLaunchAgentRuntime` function in `src/daemon/launchd.ts` was marking LaunchAgents as `missingUnit: true` whenever `launchctl print` returned a non-zero exit code. However, a non-zero exit code can also mean "service not loaded" — which is different from "unit doesn't exist."
This distinction matters for status reporting: when a user has installed the LaunchAgent but it's not currently running, we should not claim the unit is missing.
Furthermore, there was no explicit `start()` path for daemon services. When a service wasn't loaded, users only received hints about how to start it manually.
## Changes
### 1. Fixed `missingUnit` detection
- Check if plist file exists before setting `missingUnit`
- Only mark as missing if the plist file also doesn't exist
- Fixed `readLaunchAgentRuntime` to return `status: "stopped"` (not `"unknown"`) when plist exists but service is unloaded
### 2. Added `start()` method to GatewayService interface (`service.ts`)
### 3. Implemented `start()` for all three backends:
- **launchd.ts**: `enable + bootstrap + kickstart` sequence with GUI domain error handling
- **systemd.ts**: `systemctl start`
- **schtasks.ts**: `schtasks /Run`
### 4. Updated `lifecycle-core.ts`
- Now calls `service.start()` when service isn't loaded
- Falls back to hints only on error
### 5. Updated `node-service.ts`
- Added `start` wrapper with environment transformation
## Before vs After
| State | Before | After |
|-------|--------|-------|
| Plist exists, service not loaded | `missingUnit: true` ❌ | `missingUnit: false` ✅ |
| Plist doesn't exist | `missingUnit: true` ✅ | `missingUnit: true` ✅ |
| Service not loaded | Hints only ❌ | Auto-start attempt ✅ |
## Testing
- [x] `src/daemon/launchd.test.ts` — 13 tests pass
- [x] `src/cli/daemon-cli/lifecycle-core.test.ts` — 2 tests pass
- [x] `pnpm build && pnpm check && pnpm test` passes
- [ ] Tested locally with OpenClaw instance
## AI-Assisted Contribution 🤖
- [x] This PR was created with AI assistance (Claude)
- [x] Testing status: **lightly tested** — logic verified against existing tests, no new tests added
- [x] I understand what this code does: it checks plist existence before determining if a LaunchAgent unit is truly missing, and adds explicit start capability for all daemon backends
## Related
- Fixes #17763
- Supersedes #17784 (which had unrelated refactoring changes that caused merge conflicts)
- Addresses the request in #22304 (this PR now incorporates the same complete fix)
---
*Updated: Added `start()` implementation for all daemon backends (launchd, systemd, schtasks) and updated lifecycle-core to use explicit start instead of just hints.*
Most Similar PRs
#22304: Gateway: fix launchd start after stop
by apethree · 2026-02-21
78.2%
#18236: macOS daemon: bootstrap LaunchAgent on gateway start after stop
by agisilaos · 2026-02-16
77.5%
#16845: fix(daemon): gateway auto-restart on SIGTERM + agent restart guidel...
by kiminbean · 2026-02-15
71.3%
#11327: fix(launchd): reload plist from disk on restartLaunchAgent
by caiop91 · 2026-02-07
70.8%
#19857: fix(launchd): self-heal restart when service is unloaded
by vibecodooor · 2026-02-18
70.1%
#20272: fix: LaunchAgent KeepAlive causes restart loop (fixes #20257)
by MisterGuy420 · 2026-02-18
69.7%
#15619: fix: clean up orphan LaunchAgent plist on bootstrap failure
by superlowburn · 2026-02-13
69.0%
#10182: fix: skip non-openclaw LaunchAgents in doctor gateway scan
by Yida-Dev · 2026-02-06
68.9%
#20390: fix(daemon): fall back to /tmp for launchd logs on removable volumes
by lemoz · 2026-02-18
68.4%
#21591: fix(update): prevent double restart when refreshing service env
by irchelper · 2026-02-20
68.1%