ClementTsang/bottom · error · anyhow::Error

Expects a directory to be passed in.

Error message

Expects a directory to be passed in.

What it means

volume_io on Windows opens a volume path (e.g. \\.\C:\) with CreateFile to query DISK_PERFORMANCE via DeviceIoControl. The function expects the input to be a volume directory-style path; if volume.is_file() returns true, the input is rejected as it cannot be a volume root.

Solutions

  1. Ensure the path passed is a volume root ending with a path separator (e.g. C:\)
  2. Only pass paths obtained from GetLogicalDrives / volume enumeration, not arbitrary files
  3. Normalize the path to its root (path.ancestors().last() or Path::new("C:\\"))
  4. Filter enumerated paths with is_dir() checks before calling

Example fix

// before
let perf = volume_io(&Path::new("C:"))?;
// after
let root = Path::new("C:\");
assert!(root.is_dir());
let perf = volume_io(root)?;
Defensive patterns

Strategy: validation

Validate before calling

// Ensure the path is a volume root directory with a trailing separator
let p = std::path::Path::new("C:\");
if p.is_file() { panic!("not a volume root"); }
let ends_with_sep = p.as_os_str().to_string_lossy().ends_with('\\');

Type guard

fn is_volume_root(p: &std::path::Path) -> bool {
    p.is_dir() && p.parent().is_none() && p.as_os_str().to_string_lossy().ends_with('\\')
}

Try / catch

match all_volume_io() {
    Ok(map) => map,
    Err(e) if e.to_string().contains("Expects a directory") => {
        log::warn!("bad volume path given: {e}"); Default::default()
    },
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling volume_io (via all_volume_io) with a path that the system classifies as a regular file rather than a directory/volume root, e.g. passing a drive path without trailing separator or an actual file path.

Common situations: Passing 'C:' without a trailing backslash, passing mounted folder paths or file paths instead of volume roots, or misconfigured volume path lists.

Related errors


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

Appendix: source

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

    Storage::FileSystem::{
        CreateFileW, FILE_FLAGS_AND_ATTRIBUTES, FILE_SHARE_READ, FILE_SHARE_WRITE,
        FindFirstVolumeW, FindNextVolumeW, FindVolumeClose, GetVolumeNameForVolumeMountPointW,
        OPEN_EXISTING,
    },
    System::{
        IO::DeviceIoControl,
        Ioctl::{DISK_PERFORMANCE, IOCTL_DISK_PERFORMANCE},
    },
};

/// Returns the I/O for a given volume.
///
/// Based on [psutil's implementation](https://github.com/giampaolo/psutil/blob/52fe5517f716dedf9c9918e56325e49a49146130/psutil/arch/windows/disk.c#L78-L83)
/// and [heim's implementation](https://github.com/heim-rs/heim/blob/master/heim-disk/src/sys/windows/bindings/perf.rs).
fn volume_io(volume: &Path) -> anyhow::Result<DISK_PERFORMANCE> {
    if volume.is_file() {
        // We assume the volume is a directory, so bail ASAP if it isn't.
        bail!("Expects a directory to be passed in.");
    }

    let volume = {
        let mut wide_path = volume.as_os_str().encode_wide().collect::<Vec<_>>();

        // We replace the trailing backslash and replace it with a \0.
        wide_path.pop();
        wide_path.push(0x0000);

        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,

View on GitHub (pinned to b77d317502)