ClementTsang/bottom · error

Error code

Error message

Error code {err_code} - {err}

What it means

On Unix, the public `kill_process_given_pid(pid, signal)` calls `libc::kill`. On failure (non-zero return) it reads the last OS error and bails with `Error code {err_code} - {err}` where `err` is a human-readable description of the errno (ESRCH, EPERM, or EINVAL). The numeric errno accompanies the message because any libc error beyond the three mapped cases also lands here with the generic 'Unknown error occurred.' text.

Solutions

  1. Match on the errno shown in the message: 3 (ESRCH) means the process is already gone — treat as success; 1 (EPERM) means run as root/sudo or fix ownership; 22 (EINVAL) means pass a valid signal number
  2. Verify the PID is still alive before killing
  3. If EINVAL, pass a valid signal (e.g. 9 for SIGKILL, 15 for SIGTERM)
  4. Check container capabilities (CAP_KILL) or user namespaces if EPERM occurs inside Docker/Kubernetes

Example fix

// before
kill_process_given_pid(pid, signal)?;
// after
match kill_process_given_pid(pid, signal) {
    Ok(()) => {},
    Err(e) if e.to_string().contains("Error code 3") => {
        // ESRCH: process already gone, ignore
    },
    Err(e) => return Err(e),
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Unix: validate signal and liveness before libc::kill
assert!((1..=31).contains(&signal), "invalid signal {signal}");
// ESRCH check: kill(pid, 0) probes existence without sending a signal
let alive = unsafe { libc::kill(pid, 0) } == 0
    || std::io::Error::last_os_error().raw_os_error() != Some(libc::ESRCH);

Type guard

fn errno_of(err: &anyhow::Error) -> Option<i32> {
    err.chain().find_map(|c|
        c.downcast_ref::<std::io::Error>().and_then(|e| e.raw_os_error()))
}
fn is_esrch(err: &anyhow::Error) -> bool { errno_of(err) == Some(libc::ESRCH) }

Try / catch

match kill_process_given_pid(pid, signal) {
    Ok(()) => {}
    Err(e) if e.to_string().contains("Error code 3") => {} // ESRCH: gone
    Err(e) if e.to_string().contains("Error code 1") => {
        eprintln!("need elevated privileges: {e}") // EPERM
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: `libc::kill` returning -1: ESRCH (target PID does not exist), EPERM (insufficient permission to signal the target), EINVAL (invalid signal value passed as `signal`), or any other errno (e.g. EPERM in containers, init restrictions) with a known code.

Common situations: Killing a stale PID after the process exited (ESRCH); non-root user killing another user's process (EPERM); passing an out-of-range signal number like 99 (EINVAL); running inside Docker without CAP_KILL.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


AI-assisted analysis of ClementTsang/bottom@b77d317502 (2026-09-07). Data as JSON: /api/errors/2217f6600cf3fe84. Report an issue: GitHub.

Appendix: source

Thrown at src/utils/process_killer.rs:81

pub fn kill_process_given_pid(pid: Pid, signal: usize) -> anyhow::Result<()> {
    // SAFETY: the signal should be valid, and we act properly on an error (exit
    // code not 0).
    let output = unsafe { libc::kill(pid, signal as i32) };

    if output != 0 {
        // We had an error...
        let err_code = std::io::Error::last_os_error().raw_os_error();
        let err = match err_code {
            Some(libc::ESRCH) => "the target process did not exist.",
            Some(libc::EPERM) => {
                "the calling process does not have the permissions to terminate the target process(es)."
            }
            Some(libc::EINVAL) => "an invalid signal was specified.",
            _ => "Unknown error occurred.",
        };

        if let Some(err_code) = err_code {
            bail!(format!("Error code {err_code} - {err}"))
        } else {
            bail!(format!("Error code unknown - {err}"))
        };
    }

    Ok(())
}

View on GitHub (pinned to b77d317502)