pranshuparmar/witr · warning

no service label found for pid %d

Error message

no service label found for pid %d

What it means

After `launchctl blame <pid>` succeeds, GetServiceLabel trims the output; if the output is empty there is no service label to parse, so it fails with this error. An empty blame output means launchd has no recorded attribution for that pid.

Source

Thrown at internal/launchd/plist.go:72

	"/Library/LaunchDaemons",
	"/System/Library/LaunchAgents",
	"/System/Library/LaunchDaemons",
}

// GetServiceLabel uses launchctl blame to get the service label for a PID
func GetServiceLabel(pid int) (string, string, error) {
	// launchctl blame <pid> returns the service that started the process
	out, err := exec.Command("launchctl", "blame", strconv.Itoa(pid)).Output()
	if err != nil {
		return "", "", fmt.Errorf("launchctl blame failed: %w", err)
	}

	// Output format varies:
	// - "system/com.apple.example" or "gui/501/com.example.app" (real service)
	// - "speculative", "non-ipc demand", "launch job demand", "ipc (mach)" (blame reasons)
	line := strings.TrimSpace(string(out))
	if line == "" {
		return "", "", fmt.Errorf("no service label found for pid %d", pid)
	}

	// Check if this is a real service path (contains "/" and starts with domain)
	if !strings.Contains(line, "/") {
		// This is a blame reason, not a service label
		// Try to find the service by querying launchctl list
		label, domain := findServiceByPID(pid)
		if label != "" {
			return label, domain, nil
		}
		return "", "", fmt.Errorf("process not managed by a named launchd service: %s", line)
	}

	// Parse domain and label from service path
	parts := strings.SplitN(line, "/", 2)
	if len(parts) < 2 {
		return line, "", nil
	}

View on GitHub (pinned to dc4fa1da82)

Solutions

  1. Accept that the process is not launchd-managed; skip launchd enrichment and rely on plain process info.
  2. Verify the pid is still alive and re-run — a race with process exit empties output.
  3. Run as the same user (or root) as the target so blame can see its launchd session.
  4. Use the library's findServiceByPID/launchctl list fallback path or your own `launchctl list` scan as an alternative.

Example fix

// before
label, _, err := launchd.GetServiceLabel(pid)
if err != nil { log.Fatal(err) }
// after
label, _, err := launchd.GetServiceLabel(pid)
if err != nil {
    log.Printf("no launchd label for %d (likely not launchd-managed): %v", pid, err)
    label = ""
}
Defensive patterns

Strategy: fallback

Validate before calling

out, err := exec.Command("launchctl", "blame", strconv.Itoa(pid)).Output()
if err == nil && len(strings.TrimSpace(string(out))) == 0 {
    // blame will yield nothing; skip launchd enrichment up front
    return nil, nil
}

Try / catch

label, domain, err := launchd.GetServiceLabel(pid)
if err != nil && strings.Contains(err.Error(), "no service label found") {
    log.Printf("pid %d is not launchd-managed; continuing without service info", pid)
    return defaultProcessInfo(pid)
}
if err != nil { return err }

Prevention

When it happens

Trigger: Calling GetServiceLabel for a pid whose blame output is empty: the process was not started by launchd (e.g. started from a shell/IDE), the pid just exited, or blame returned nothing for an unmanaged/system-transient process.

Common situations: Inspecting a dev server started manually in a terminal; processes spawned by sshd sessions; querying a pid that died between listing and blame; launchd sessions (gui/system) where blame yields no data for the calling user.

Related errors


AI-assisted analysis of pranshuparmar/witr@dc4fa1da82 (2026-09-01). Data as JSON: /api/errors/d2451d26099e4495. Report an issue: GitHub.