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

  1. Treat as benign for a process monitor: skip the PID and continue enumerating
  2. Verify the PID is still alive before calling (e.g. OpenProcess itself, or a fresh process list)
  3. Check the OS error from the preceding OpenProcess `?` to distinguish access-denied from nonexistent PID
  4. 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

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


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)