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
- Accept that the process is not launchd-managed; skip launchd enrichment and rely on plain process info.
- Verify the pid is still alive and re-run — a race with process exit empties output.
- Run as the same user (or root) as the target so blame can see its launchd session.
- 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
- Only expect labels for genuinely launchd-spawned processes; shell/IDE-launched apps have none.
- Treat this as informational (warning) rather than fatal in inspection tools.
- Run in the same user session as the target for reliable blame output.
- Re-check pid liveness — dying processes produce empty output.
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
- launchctl blame failed: %w
- process not managed by a named launchd service: %s
- listfds returned partial record
- failed to convert plist: %w
- no process ancestry found
AI-assisted analysis of pranshuparmar/witr@dc4fa1da82 (2026-09-01).
Data as JSON: /api/errors/d2451d26099e4495.
Report an issue: GitHub.