ClementTsang/bottom · error · anyhow::Error

Handle error

Error message

Handle error: {err:?}

What it means

volume_io calls CloseHandle(h_device) after the DeviceIoControl query and bails if the close itself fails. A failing CloseHandle on a volume handle means the kernel refused to release the handle (the error is carried from the windows crate's Err variant). The disk performance data is discarded even though it was likely retrieved successfully.

Solutions

  1. Retry the volume_io call; this is usually a transient handle-state race
  2. Avoid calling disk I/O collection concurrently for the same volume from multiple threads
  3. Check for third-party storage filter drivers (antivirus/encryption) interfering with device handles
  4. Log and continue: the performance data was valid, so the close failure is rarely fatal to the caller
Defensive patterns

Strategy: retry

Try / catch

// close failure is usually transient — retry the query once
match volume_io(&volume) {
    Ok(stats) => stats,
    Err(e) if e.to_string().contains("Handle error") => volume_io(&volume)
        .unwrap_or_else(|e2| { log::debug!("{e2:?}"); Default::default() }),
    Err(e) => { log::debug!("{e:?}"); Default::default() }
}

Prevention

When it happens

Trigger: CloseHandle returns Err — e.g. the handle was already invalidated/closed, an ERROR_INVALID_HANDLE from kernel-state races, or handle-table corruption under heavy concurrent volume queries.

Common situations: Monitoring daemons querying many volumes in tight loops with multiple threads touching the same volume handle table; rare kernel races on systems under extreme load; misbehaving filter drivers interfering with device handles.

Related errors


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

Appendix: source

Thrown at src/collection/disks/windows/bindings.rs:83

    // SAFETY: This should be safe, we'll manually check the results and the
    // arguments should be valid.
    let ret = unsafe {
        DeviceIoControl(
            h_device,
            IOCTL_DISK_PERFORMANCE,
            None,
            0,
            Some(&mut disk_performance as *mut _ as _),
            mem::size_of::<DISK_PERFORMANCE>() as u32,
            Some(&mut bytes_returned),
            None,
        )
    };

    // SAFETY: This should be safe, we will check the result as well.
    let handle_result = unsafe { CloseHandle(h_device) };
    if let Err(err) = handle_result {
        bail!("Handle error: {err:?}");
    }

    if let Err(err) = ret {
        bail!("Device I/O error: {err:?}");
    } else {
        Ok(disk_performance)
    }
}

fn current_volume(buffer: &[u16]) -> PathBuf {
    let first_null = buffer.iter().position(|byte| *byte == 0x00).unwrap_or(0);
    let path_string = OsString::from_wide(&buffer[..first_null]);

    PathBuf::from(path_string)
}

fn close_find_handle(handle: HANDLE) -> anyhow::Result<()> {
    // Clean up the handle.

View on GitHub (pinned to b77d317502)