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

  1. Treat it as a benign race: skip the PID and continue with the rest of the process list
  2. Re-check the process exists (e.g. via kill(pid, 0)) before fetching kinfo_proc
  3. Check errno via std::io::Error::last_os_error() to distinguish ESRCH (gone) from EPERM (permissions)
  4. 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

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


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)