ClementTsang/bottom · error
PID for was not found
Error message
PID for {pid_path:?} was not found What it means
When building a Process from a /proc/<pid> directory, the PID is recovered from the directory entry: either the file name itself or, for numeric-task threads, the target of the symlink via readlinkat. If neither yields a value parseable as a Pid, the library raises this error because a process cannot be represented without its PID.
Solutions
- Skip the unreadable entry during enumeration — the thread/process likely exited between listing and opening.
- Verify the path is a genuine /proc/<pid> or /proc/<pid>/task/<tid> directory before calling Process::from_path.
- Check that the process still exists (readdir race); re-scan /proc to refresh the entry list.
Example fix
// before
let proc = Process::from_path(pid_dir).unwrap();
// after
match Process::from_path(&pid_dir) {
Ok(p) => processes.push(p),
Err(e) if e.to_string().contains("was not found") => {
// thread/process vanished mid-scan; skip it
}
Err(e) => return Err(e.into()),
} Defensive patterns
Strategy: fallback
Validate before calling
fn pid_from_dir(path: &Path) -> Option<Pid> {
path.file_name()
.and_then(|n| n.to_str())
.and_then(|n| n.parse::<Pid>().ok())
.or_else(|| std::fs::read_link(path).ok()
.and_then(|t| t.to_str().and_then(|s| s.parse().ok())))
}
if pid_from_dir(&dir).is_none() { skip_entry(); } Type guard
fn dir_is_process(dir: &Path) -> bool {
dir.file_name()
.and_then(|n| n.to_str())
.map(|n| n.parse::<i32>().is_some())
.unwrap_or(false)
} Try / catch
match Process::from_path(&pid_dir) {
Ok(p) => Some(p),
Err(e) if e.to_string().contains("was not found") => None,
Err(e) => return Err(e.into()),
} Prevention
- Filter directory listings to numeric names before constructing Processes
- Expect races: any /proc entry may disappear between readdir and open; skip those
- In containers, verify /proc is a real procfs mount, not a masked or stubbed path
When it happens
Trigger: Calling Process::from_path on a directory whose name is non-numeric and whose readlink does not resolve to a numeric PID — e.g. a /proc/<pid>/task entry that vanished, a fake/stub procfs, or a directory with an unexpected name.
Common situations: Scanning /proc/[pid]/task while threads terminate (dirfd points at a removed entry); running in restricted containers where readlink fails on procfs; non-standard procfs mounts or mocked filesystems used in tests.
Understand the failure class
Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.
Related errors
- start paren missing
- stat string is malformed
- missing state
- Unable to open zfs proc directory
- Unexpected 'ps' output
AI-assisted analysis of ClementTsang/bottom@b77d317502 (2026-09-07).
Data as JSON: /api/errors/4eec1d981eadec3a.
Report an issue: GitHub.
Appendix: source
Thrown at src/collection/processes/linux/process.rs:301
let pid_dir = rustix::fs::openat(
rustix::fs::CWD,
pid_path.as_path(),
OFlags::PATH | OFlags::DIRECTORY | OFlags::CLOEXEC,
Mode::empty(),
)?;
let pid = pid_path
.as_path()
.components()
.next_back()
.and_then(|s| s.to_string_lossy().parse::<Pid>().ok())
.or_else(|| {
rustix::fs::readlinkat(rustix::fs::CWD, pid_path.as_path(), vec![])
.ok()
.and_then(|s| s.to_string_lossy().parse::<Pid>().ok())
})
.ok_or_else(|| anyhow!("PID for {pid_path:?} was not found"))?;
let uid = {
let metadata = rustix::fs::fstat(&pid_dir);
match metadata {
Ok(md) => Some(md.st_uid),
Err(_) => None,
}
};
let mut root = pid_path;
// NB: Whenever you add a new stat, make sure to pop the root and clear
// the buffer!
// Stat is pretty long, do this first to pre-allocate up-front.
let stat =
open_at(&mut root, "stat", &pid_dir).and_then(|file| Stat::from_file(file, buffer))?;
reset(&mut root, buffer);View on GitHub (pinned to b77d317502)