jdx/mise · error

`launchctl {}` failed: {}

Error message

`launchctl {}` failed: {}

What it means

A `launchctl ...` subprocess spawned by mise's launchd backend (bootstrap, bootout, kickstart, enable/disable, etc.) exited with a non-zero status. The message embeds the exact joined argv and launchctl's trimmed stderr, so the launchd error text (e.g. 'Load failed: 5: Input/output error' or 'No such process') is the real diagnostic. Note the async `?` on `.output()` maps spawn/IO failures to a different error; this bail is specifically for a completed process with a failing exit status.

Source

Thrown at src/system/launchd.rs:513

        .stdin(Stdio::null())
        .stdout(Stdio::null())
        .stderr(Stdio::null())
        .status()
        .await?;
    Ok(output.success())
}

async fn launchctl(args: &[String]) -> Result<()> {
    debug!("$ launchctl {}", shell_words::join(args));
    let output = tokio::process::Command::new("launchctl")
        .args(args)
        .stdin(Stdio::null())
        .stdout(Stdio::piped())
        .stderr(Stdio::piped())
        .output()
        .await?;
    if !output.status.success() {
        bail!(
            "`launchctl {}` failed: {}",
            shell_words::join(args),
            String::from_utf8_lossy(&output.stderr).trim()
        );
    }
    Ok(())
}

async fn bootout(domain: &str, path: &Path) -> Result<()> {
    let args = [
        "bootout".to_string(),
        domain.to_string(),
        path.to_string_lossy().to_string(),
    ];
    match launchctl(&args).await {
        Ok(()) => Ok(()),
        Err(err) if bootout_missing_error(&err.to_string()) => Ok(()),
        Err(err) => Err(err),

View on GitHub (pinned to 9dcfcaa0dc)

Solutions

  1. Read the embedded launchctl stderr — it names the actual OS-level failure; fix that first
  2. Verify the domain and path: user agents need `gui/$(id -u)`, system daemons need `sudo` and the `system` domain
  3. Check the generated plist at ~/Library/LaunchAgents/dev.mise.<name>.plist (or /Library/LaunchDaemons) with `plutil -lint` and compare against the loaded version via `launchctl print`
  4. If the service is in a wedged state, run `launchctl bootout <domain> <plist-or-label>` manually once, then re-run mise
  5. Re-run with MISE_DEBUG=1 to see the exact `$ launchctl ...` line being executed

Example fix

# before: wrong domain for a LaunchAgent, launchctl exits non-zero
launchctl bootstrap system ~/Library/LaunchAgents/dev.mise.backup.plist

# after: bootstrap into the user's GUI domain
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/dev.mise.backup.plist
Defensive patterns

Strategy: retry

Validate before calling

# Pre-flight the operations mise will perform
launchctl print "gui/$(id -u)/dev.mise.<name>" >/dev/null 2>&1 || echo "agent not loaded yet"
plutil -lint ~/Library/LaunchAgents/dev.mise.<name>.plist  # plist is valid

Try / catch

// Rust callers wrapping mise's system module: distinguish process failure from spawn failure
match launchctl(&args).await {
    Ok(()) => {}
    Err(report) if report.to_string().starts_with("`launchctl") => {
        // completed with non-zero exit: stderr is embedded — surface it, consider one retry after bootout
        eprintln!("launchctl rejected: {report}");
    }
    Err(e) => { /* spawn/IO failure — different remediation */ }
}

Prevention

When it happens

Trigger: Running `mise bootstrap apply` (or service status/sync flows) where mise executes e.g. `launchctl bootstrap gui/501 /Library/LaunchDaemons/dev.mise.x.plist` or `launchctl bootout gui/501 ...` and launchctl rejects it: malformed/generated plist, label mismatch, operation not permitted (daemon vs gui domain), service not currently loaded when kicking out, or SIP-protected locations.

Common situations: Editing a managed plist by hand so the on-disk copy diverges from what launchd loaded; targeting the wrong domain type (gui/<uid> vs system) for a daemon; running without sudo where LaunchDaemons require root; macOS changing bootstrap semantics across versions; a BootOut on an already-booted-out service on newer macOS which returns an error.

Related errors


AI-assisted analysis of jdx/mise@9dcfcaa0dc (2026-08-17). Data as JSON: /api/errors/5efa25a78d86e0d4. Report an issue: GitHub.