ClementTsang/bottom · error
failed to get process for pid
Error message
failed to get process for pid {pid} What it means
kinfo_process calls libc::sysctl with CTL_KERN/KERN_PROC/KERN_PROC_PID to fetch the kinfo_proc for a PID, and bails when sysctl returns < 0 (errno set). This means the kernel could not return process information for the requested PID. The library uses this as the signal that the process record is unobtainable on macOS.
Solutions
- Treat it as a benign race: skip the PID and continue with the rest of the process list
- Re-check the process exists (e.g. via kill(pid, 0)) before fetching kinfo_proc
- Check errno via std::io::Error::last_os_error() to distinguish ESRCH (gone) from EPERM (permissions)
- If EPERM is persistent, run with fewer restrictions or accept partial process data
Example fix
// before
if result < 0 {
bail!("failed to get process for pid {pid}");
}
// after
if result < 0 {
let e = io::Error::last_os_error();
if e.kind() == io::ErrorKind::NotFound || e.raw_os_error() == Some(libc::ESRCH) {
return Err(ProcessGone(pid).into()); // skip gracefully
}
bail!("failed to get process for pid {pid}: {e}");
} Defensive patterns
Strategy: try-catch
Validate before calling
// check the process is alive before fetching kinfo_proc
fn pid_alive(pid: i32) -> bool {
unsafe { libc::kill(pid, 0) == 0 }
} Try / catch
match kinfo_process(pid) {
Ok(info) => Some(info),
Err(e) if e.to_string().contains("failed to get process") => None, // raced exit
Err(e) => return Err(e),
} Prevention
- Treat per-PID sysctl failures as expected races, not fatal errors
- Re-list PIDs from a fresh snapshot rather than caching them
- Distinguish ESRCH from EPERM by inspecting errno for better skip/retry decisions
When it happens
Trigger: sysctl(KERN_PROC_PID, pid) returns -1 — the PID does not exist (ESRCH), the caller lacks permission for that process (EPERM, e.g. other users' processes under SIP), or an invalid pid value was passed.
Common situations: Race between listing PIDs via KERN_PROC_ALL and fetching details — the process exited in between; querying PID 0 or kernel-owned PIDs; sandboxed builds where sysctl process introspection is restricted.
Understand the failure class
Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.
Related errors
- Unexpected 'ps' output
- IOServiceGetMatchingServices failed, error code
- IORegistryEntryCreateCFProperties failed, error code
- IORegistryEntryGetParentEntry failed, error code
- Failed to open process with PID
AI-assisted analysis of ClementTsang/bottom@b77d317502 (2026-09-07).
Data as JSON: /api/errors/e6535fc3d5f2ea7a.
Report an issue: GitHub.
Appendix: source
Thrown at src/collection/processes/macos/sysctl_bindings.rs:283
pub(crate) fn kinfo_process(pid: Pid) -> Result<kinfo_proc> {
let mut name: [i32; 4] = [CTL_KERN, KERN_PROC, KERN_PROC_PID, pid];
let mut size = mem::size_of::<kinfo_proc>();
let mut info = mem::MaybeUninit::<kinfo_proc>::uninit();
// SAFETY: libc binding, we assume all arguments are valid.
let result = unsafe {
libc::sysctl(
name.as_mut_ptr(),
4,
info.as_mut_ptr() as *mut libc::c_void,
&mut size,
std::ptr::null_mut(),
0,
)
};
if result < 0 {
bail!("failed to get process for pid {pid}");
}
// sysctl succeeds but size is zero, happens when process has gone away
if size == 0 {
bail!("failed to get process for pid {pid}");
}
// SAFETY: info is initialized if result succeeded and returned a
// non-negative result. If sysctl failed, it returns -1 with errno set.
//
// Source: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/sysctl.3.html
unsafe { Ok(info.assume_init()) }
}
#[cfg(test)]
mod test {
use std::mem;
View on GitHub (pinned to b77d317502)