nikivdev/code · error

Failed to disable service: {}

Error message

Failed to disable service: {}

What it means

disable_service runs an external `launchctl` command and this error is raised when the command exits non-zero; the captured stderr is embedded in the message. It means launchctl itself rejected the disable/bootout operation, not that the tool found no service.

Source

Thrown at src/macos.rs:725

        }
    }

    // Then disable
    let target = format!("{}/{}", domain, svc.id);
    let mut cmd = if svc.service_type.requires_sudo() {
        let mut c = Command::new("sudo");
        c.args(["launchctl", "disable", &target]);
        c
    } else {
        let mut c = Command::new("launchctl");
        c.args(["disable", &target]);
        c
    };

    let output = cmd.output()?;
    if !output.status.success() {
        let stderr = String::from_utf8_lossy(&output.stderr);
        bail!("Failed to disable service: {}", stderr);
    }

    Ok(())
}

/// Enable a launchd service.
fn enable_service(svc: &LaunchdService) -> Result<()> {
    let domain = svc.service_type.domain();

    // First enable
    let target = format!("{}/{}", domain, svc.id);
    let mut cmd = if svc.service_type.requires_sudo() {
        let mut c = Command::new("sudo");
        c.args(["launchctl", "enable", &target]);
        c
    } else {
        let mut c = Command::new("launchctl");
        c.args(["enable", &target]);

View on GitHub (pinned to a747e741ae)

Solutions

  1. Read the embedded stderr in the message — it contains launchctl's actual reason.
  2. Re-run the disable command with sudo if the service lives in the system domain.
  3. Verify the service is currently loaded: `launchctl print <domain>/<label>`; if already unloaded there is nothing to disable.
  4. Confirm the plist path and label are correct before retrying.

Example fix

// before
flow macos disable my-daemon               // fails: not loaded / no sudo
// after
sudo flow macos disable my-daemon          // or ensure service is loaded first
Defensive patterns

Strategy: try-catch

Validate before calling

let loaded = std::process::Command::new("launchctl")
    .args(["print", &format!("gui/{}/{}", unsafe { libc::geteuid() }, label)])
    .status()
    .map(|s| s.success())
    .unwrap_or(false);
if !loaded { eprintln!("service not loaded; nothing to disable"); return; }

Type guard

fn is_launchctl_stderr(err: &anyhow::Error) -> bool {
    err.to_string().starts_with("Failed to disable service: ")
}

Try / catch

match macos::run_disable(opts) {
    Err(e) if e.to_string().contains("Failed to disable service:") => {
        let stderr = e.to_string();
        eprintln!("launchctl said: {stderr}");
        if stderr.contains("Operation not permitted") { /* rerun with sudo */ }
    }
    other => other?,
}

Prevention

When it happens

Trigger: launchctl fails to bootout the service: service not currently loaded, wrong domain (user vs system), sudo required but not used, or malformed plist path.

Common situations: Disabling a system-level service without sudo; the service was already unloaded; passing the wrong label/domain so launchctl cannot find the service.

Related errors


AI-assisted analysis of nikivdev/code@a747e741ae (2026-09-01). Data as JSON: /api/errors/f158c808ee5bed86. Report an issue: GitHub.