ClementTsang/bottom · error · anyhow::Error
Invalid handle value
Error message
Invalid handle value: {:?} What it means
In volume_io, CreateFileW opened the volume path (\\.\C:) but returned a handle that fails is_invalid() (INVALID_HANDLE_VALUE), so the code bails with the last OS error. The library treats an invalid volume handle as fatal because the subsequent DeviceIoControl(IOCTL_DISK_PERFORMANCE) call needs a valid handle. It typically means the volume path could not be opened even though the Win32 call itself reported success-shaped behavior.
Solutions
- Check the logged io::Error::last_os_error() for the underlying Win32 error code (e.g. ERROR_ACCESS_DENIED, ERROR_FILE_NOT_FOUND) and fix that condition
- Re-run the collection; transient cases (volume removed mid-iteration) resolve on the next pass
- Ensure the process runs with sufficient privileges (Administrator) to open volume devices
- Filter out volumes that fail to open instead of failing the entire all_volume_io scan
Example fix
// before
if h_device.is_invalid() {
bail!("Invalid handle value: {:?}", io::Error::last_os_error());
}
// after
if h_device.is_invalid() {
eprintln!("skipping volume {}: {:?}", volume.display(), io::Error::last_os_error());
return Ok(None); // or continue to the next volume
} Defensive patterns
Strategy: try-catch
Validate before calling
// Windows: check the volume path is a \\?\Volume{...}\ device path before querying
fn is_volume_guid_path(p: &str) -> bool {
p.starts_with("\\\\?\\Volume{") && p.ends_with("\\")
} Type guard
fn handle_valid(h: HANDLE) -> bool { !h.is_invalid() } Try / catch
match all_volume_io() {
Ok(results) => results,
Err(e) => { log::warn!("volume io unavailable: {e:?}"); Vec::new() }
} Prevention
- Run collectors with enough privilege to open volume devices
- Re-enumerate volumes immediately before querying instead of caching paths
- Skip failed volumes rather than failing the whole scan
When it happens
Trigger: CreateFileW on a volume GUID path returns INVALID_HANDLE_VALUE — e.g. the volume has been removed/dismounted between FindFirstVolumeW enumeration and the open, the path lacks the correct \\?\ volume GUID prefix, or access to the device is denied in a way surfaced as an invalid handle.
Common situations: Enumerating disk performance stats on machines where a USB/network volume disappears mid-scan; running in environments (services, containers, restricted tokens) without rights to open volume devices; calling volume_io with a hand-crafted path missing the trailing-backslash-to-NUL normalization the function performs.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Handle error
- Device I/O error
- Error while iterating over volumes
- Could not get volume name for mount point
- Failed to open process with PID
AI-assisted analysis of ClementTsang/bottom@b77d317502 (2026-09-07).
Data as JSON: /api/errors/158f12fc9230be8f.
Report an issue: GitHub.
Appendix: source
Thrown at src/collection/disks/windows/bindings.rs:59
wide_path
};
// SAFETY: API call, arguments should be correct. We must also check after
// the call to ensure it is valid.
let h_device = unsafe {
CreateFileW(
windows::core::PCWSTR(volume.as_ptr()),
0,
FILE_SHARE_READ | FILE_SHARE_WRITE,
None,
OPEN_EXISTING,
FILE_FLAGS_AND_ATTRIBUTES(0),
Some(Foundation::HANDLE::default()),
)?
};
if h_device.is_invalid() {
bail!("Invalid handle value: {:?}", io::Error::last_os_error());
}
let mut disk_performance = DISK_PERFORMANCE::default();
let mut bytes_returned = 0;
// 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,
)View on GitHub (pinned to b77d317502)