ClementTsang/bottom · warning

Failed to get priority class for process with PID

Error message

Failed to get priority class for process with PID {pid}

What it means

After opening the process handle successfully, get_priority calls GetPriorityClass, which per Microsoft docs returns zero on failure, and the library bails. The handle was valid, but the kernel could not report the priority class. The handle is leaked at this bail point (CloseHandle happens after the check).

Solutions

  1. Skip the PID — exit races are expected in process monitoring; retry on the next collection cycle
  2. Close the handle in all paths (use a guard/defer pattern) so the bail does not leak the handle
  3. Re-fetch a fresh process list instead of reusing stale PIDs from an earlier snapshot
  4. Log at debug level rather than erroring the entire sysinfo_process_data collection

Example fix

// before
let priority = GetPriorityClass(process_handle);
if priority == 0 {
    bail!("Failed to get priority class for process with PID {pid}");
}
// after
let priority = GetPriorityClass(process_handle);
let _ = CloseHandle(process_handle); // always release
if priority == 0 {
    return Ok(0); // process exited mid-query; skip
}
Defensive patterns

Strategy: try-catch

Try / catch

match get_priority(pid) {
    Ok(p) => p,
    Err(e) if e.to_string().contains("priority class") => 0, // exit race
    Err(e) => { log::debug!("{e}"); 0 }
}

Prevention

When it happens

Trigger: GetPriorityClass(process_handle) returns 0 — the process exited between OpenProcess and the query (handle now points to a dead process), or an internal query failure on a terminating process.

Common situations: Rapid process churn: short-lived processes dying in the microseconds between opening the handle and querying the class; batch-collecting priority for every PID on a busy system.

Related errors


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

Appendix: source

Thrown at src/collection/processes/windows.rs:30

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.
pub fn sysinfo_process_data(
    collector: &mut DataCollector,
) -> CollectionResult<Vec<ProcessHarvest>> {
    let sys = &collector.sys.system;
    let users = &collector.sys.users;
    let use_current_cpu_total = collector.use_current_cpu_total;

View on GitHub (pinned to b77d317502)