{"record":{"id":"7c025adf6522b815","repo":"pranshuparmar/witr","slug":"launchctl-blame-failed-w","errorCode":null,"errorMessage":"launchctl blame failed: %w","messagePattern":"launchctl blame failed: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/launchd/plist.go","lineNumber":64,"sourceCode":"\tArray   []string\n\tDict    *plistDict\n}\n\n// plist search paths in order of precedence\nvar plistSearchPaths = []string{\n\t\"~/Library/LaunchAgents\",\n\t\"/Library/LaunchAgents\",\n\t\"/Library/LaunchDaemons\",\n\t\"/System/Library/LaunchAgents\",\n\t\"/System/Library/LaunchDaemons\",\n}\n\n// GetServiceLabel uses launchctl blame to get the service label for a PID\nfunc GetServiceLabel(pid int) (string, string, error) {\n\t// launchctl blame <pid> returns the service that started the process\n\tout, err := exec.Command(\"launchctl\", \"blame\", strconv.Itoa(pid)).Output()\n\tif err != nil {\n\t\treturn \"\", \"\", fmt.Errorf(\"launchctl blame failed: %w\", err)\n\t}\n\n\t// Output format varies:\n\t// - \"system/com.apple.example\" or \"gui/501/com.example.app\" (real service)\n\t// - \"speculative\", \"non-ipc demand\", \"launch job demand\", \"ipc (mach)\" (blame reasons)\n\tline := strings.TrimSpace(string(out))\n\tif line == \"\" {\n\t\treturn \"\", \"\", fmt.Errorf(\"no service label found for pid %d\", pid)\n\t}\n\n\t// Check if this is a real service path (contains \"/\" and starts with domain)\n\tif !strings.Contains(line, \"/\") {\n\t\t// This is a blame reason, not a service label\n\t\t// Try to find the service by querying launchctl list\n\t\tlabel, domain := findServiceByPID(pid)\n\t\tif label != \"\" {\n\t\t\treturn label, domain, nil\n\t\t}","sourceCodeStart":46,"sourceCodeEnd":82,"githubUrl":"https://github.com/pranshuparmar/witr/blob/dc4fa1da82d3e266fcbd928641b4f30b3077c64f/internal/launchd/plist.go#L46-L82","documentation":"GetServiceLabel runs `launchctl blame <pid>` to discover which launchd service owns a process. Any non-zero exit or failure to capture output from that external command is wrapped in this error. Since launchctl blame only works on macOS and requires appropriate privileges, environment issues commonly surface here.","triggerScenarios":"Calling GetLaunchdInfo/GetServiceLabel on macOS when launchctl blame exits non-zero: pid does not exist, insufficient privileges, launchctl not present, or non-macOS platform.","commonSituations":"Running on Linux/CI (no launchctl in PATH); inspecting a process owned by another user without sudo; the target pid exited between discovery and the blame call; sandboxed environments that block exec of launchctl.","solutions":["Run on macOS and ensure launchctl exists (`which launchctl`).","Retry with elevated privileges (sudo) if the target process belongs to another user.","Confirm the pid is still alive before calling (signal 0 check).","If blame is unsupported for that process, fall back to parsing the process environment or skip launchd enrichment.","Inspect the wrapped %w error to distinguish exec-not-found from command failure."],"exampleFix":"// before\nlabel, domain, err := launchd.GetServiceLabel(pid) // fails on Linux\n// after\nif runtime.GOOS != \"darwin\" {\n    return nil, fmt.Errorf(\"launchd info only available on macOS\")\n}\nlabel, domain, err := launchd.GetServiceLabel(pid)","handlingStrategy":"fallback","validationCode":"if runtime.GOOS != \"darwin\" {\n    return errors.New(\"launchd enrichment requires macOS\")\n}\nif _, err := exec.LookPath(\"launchctl\"); err != nil {\n    return errors.New(\"launchctl not available in PATH\")\n}","typeGuard":null,"tryCatchPattern":"label, domain, err := launchd.GetServiceLabel(pid)\nif err != nil {\n    var ee *exec.ExitError\n    if errors.As(err, &ee) && strings.Contains(err.Error(), \"launchctl blame failed\") {\n        log.Printf(\"blame unavailable for pid %d (privileges or unsupported): %v\", pid, err)\n        return fallbackEnrichment(pid) // e.g. ps/env-based info\n    }\n    return err\n}","preventionTips":["Gate launchd calls behind runtime.GOOS == \"darwin\" checks.","Run with sudo when inspecting other users' processes.","Check the pid is alive immediately before calling.","Inspect the wrapped %w error to distinguish missing launchctl from command failure."],"tags":["macos","launchd","external-command","process-inspection"],"backgroundTag":"launchctl-blame-failed","analyzedSha":"dc4fa1da82d3e266fcbd928641b4f30b3077c64f","analyzedAt":"2026-09-01T12:17:08.767Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}