ClementTsang/bottom · error
Failed to open process with PID
Error message
Failed to open process with PID {pid} to get priority class What it means
get_priority opens a target process with OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION) and bails if the returned handle is invalid. Without a valid process handle the library cannot query the priority class via GetPriorityClass. This reflects the OS refusing (or being unable) to grant even limited query access to the PID.
Solutions
- Treat as benign for a process monitor: skip the PID and continue enumerating
- Verify the PID is still alive before calling (e.g. OpenProcess itself, or a fresh process list)
- Check the OS error from the preceding OpenProcess `?` to distinguish access-denied from nonexistent PID
- Run elevated only if you genuinely need data on protected/system processes
Example fix
// before
let process_handle: HANDLE = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, false, pid)?;
if process_handle.is_invalid() {
bail!("Failed to open process with PID {pid} to get priority class");
}
// after
let process_handle: HANDLE = match OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, false, pid) {
Ok(h) if !h.is_invalid() => h,
_ => return Ok(0), // or skip: process gone / protected
}; Defensive patterns
Strategy: validation
Validate before calling
// confirm the PID exists in a fresh process snapshot before querying priority
fn pid_in_snapshot(pid: u32, snapshot: &[u32]) -> bool { snapshot.contains(&pid) } Type guard
fn handle_valid(h: HANDLE) -> bool { !h.is_invalid() } Try / catch
match get_priority(pid) {
Ok(p) => p,
Err(e) if e.to_string().contains("Failed to open process") => 0, // skip
Err(e) => { log::debug!("{e}"); 0 }
} Prevention
- Always derive PIDs from a freshly taken process list
- Expect access-denied on protected/system processes and skip them
- Never pass PID 0 or system PIDs to priority queries
When it happens
Trigger: OpenProcess returns an invalid (NULL) handle — the PID no longer exists (ERROR_INVALID_PARAMETER), access is denied (ERROR_ACCESS_DENIED for protected/system processes), or the PID value is malformed.
Common situations: Querying PIDs captured in an earlier snapshot after the processes exited; inspecting protected processes (antivirus, system-critical, PPL processes) that block even PROCESS_QUERY_LIMITED_INFORMATION; passing 0 or kernel PIDs.
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
- Invalid handle value
- Handle error
- Failed to get priority class for process with PID
- Unexpected 'ps' output
- Expects a directory to be passed in.
AI-assisted analysis of ClementTsang/bottom@b77d317502 (2026-09-07).
Data as JSON: /api/errors/71fe7d4909e0714a.
Report an issue: GitHub.
Appendix: source
Thrown at src/collection/processes/windows.rs:24
use itertools::Itertools;
use windows::Win32::{
Foundation::{CloseHandle, HANDLE},
System::Threading::{GetPriorityClass, OpenProcess, PROCESS_QUERY_LIMITED_INFORMATION},
};
use super::{ProcessHarvest, process_status_str};
use crate::collection::{DataCollector, error::CollectionResult};
/// See [here](https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getpriorityclass)
/// for more information on the core Windows API being called and the meaning of
/// the priorities, as well as the access rights needed.
fn get_priority(pid: u32) -> anyhow::Result<i32> {
// SAFETY: We check validity of each step and bail on errors. We also close
// the handle.
unsafe {
let process_handle: HANDLE = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, false, pid)?;
if process_handle.is_invalid() {
bail!("Failed to open process with PID {pid} to get priority class");
}
// From docs: "If the function fails, the return value is zero."
let priority = GetPriorityClass(process_handle);
if priority == 0 {
bail!("Failed to get priority class for process with PID {pid}");
}
let handle_result = CloseHandle(process_handle);
if let Err(err) = handle_result {
bail!(err);
}
Ok(priority as i32)
}
}
// TODO: There's a lot of shared code with this and the unix impl.View on GitHub (pinned to b77d317502)