zeroclaw-labs/zeroclaw · critical · anyhow::Error
launchctl start failed: {}
Error message
launchctl start failed: {} What it means
Thrown by maybe_restart_managed_daemon_service on macOS when the com.zeroclaw.daemon launchd job is installed (plist exists and appears in `launchctl list`) and an unconditional `launchctl stop` is followed by `launchctl start com.zeroclaw.daemon`, but start exits non-zero. The daemon was already stopped, so this leaves the service down until the failure is addressed; callers print a manual-restart hint when this bubbles up.
Source
Thrown at crates/zeroclaw-channels/src/orchestrator/mod.rs:8771
let list_output = Command::new("launchctl")
.arg("list")
.output()
.context("Failed to query launchctl list")?;
let listed = String::from_utf8_lossy(&list_output.stdout);
if !listed.contains("com.zeroclaw.daemon") {
return Ok(false);
}
let _ = Command::new("launchctl")
.args(["stop", "com.zeroclaw.daemon"])
.output();
let start_output = Command::new("launchctl")
.args(["start", "com.zeroclaw.daemon"])
.output()
.context("Failed to start launchd daemon service")?;
if !start_output.status.success() {
let stderr = String::from_utf8_lossy(&start_output.stderr);
anyhow::bail!("launchctl start failed: {}", stderr.trim());
}
return Ok(true);
}
if cfg!(target_os = "linux") {
// OpenRC (system-wide) takes precedence over systemd (user-level)
let openrc_init_script = PathBuf::from("/etc/init.d/zeroclaw");
if openrc_init_script.exists()
&& let Ok(status_output) = Command::new("rc-service").args(OPENRC_STATUS_ARGS).output()
{
// rc-service exits 0 if running, non-zero otherwise
if status_output.status.success() {
let restart_output = Command::new("rc-service")
.args(OPENRC_RESTART_ARGS)
.output()
.context("Failed to restart OpenRC daemon service")?;
if !restart_output.status.success() {View on GitHub (pinned to 88bb9c8533)
Solutions
- Bring the daemon back first: `zeroclaw service stop && zeroclaw service start` (or `launchctl start com.zeroclaw.daemon`) and read the printed stderr — it names the launchd failure
- Verify the plist's program path still points at the installed zeroclaw binary; reinstall the service if the binary moved
- Validate the daemon starts manually: run the binary in the foreground and fix any config error that crashes it on boot
- Check `launchctl list | grep zeroclaw` and system log (`log show --predicate 'process == "launchd"'`) for job-load errors
Example fix
# before: stale plist after reinstall # launchctl start failed: ... (program path no longer exists) # after zeroclaw service stop zeroclaw service start # re-installs plist with current binary path
Defensive patterns
Strategy: fallback
Validate before calling
// Verify the daemon job is healthy before the operation that triggers auto-restart:
let listed = Command::new("launchctl").arg("list").output()?;
if !String::from_utf8_lossy(&listed.stdout).contains("com.zeroclaw.daemon") {
return Ok(()); // not managed by launchd; nothing to break
} Try / catch
if let Err(e) = maybe_restart_managed_daemon_service() {
eprintln!("auto-restart failed: {e:#}; run `zeroclaw service stop && zeroclaw service start`");
// config was already saved — do not roll it back, just surface the manual step
} Prevention
- Keep the launchd plist in sync with the binary location; re-run service install after upgrades
- Smoke-test the daemon in the foreground before loading it into launchd
- Monitor for the daemon being stopped after any config-save flow, since stop precedes start
When it happens
Trigger: Saving a config that triggers an automatic daemon reload (e.g. the allowlist save path around line 8730) on macOS while `launchctl start` fails — common causes: the plist's program path is stale after a reinstall, the binary moved, or launchd rejects the job definition.
Common situations: ZeroClaw binary was upgraded/relocated but ~/Library/LaunchAgents/com.zeroclaw.daemon.plist still points at the old path; plist hand-edited into an invalid state; macOS sandbox/permissions blocking launchctl for the session; daemon crashed at startup due to bad config so start immediately fails.
Related errors
- rc-service restart failed: {}
- systemctl restart failed: {}
- daemon child exited with status {status}
- the launchd daemon runner is only supported on macOS
- Gateway responded {status}: {err}
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/c6560d0e049eef83.
Report an issue: GitHub.