ClementTsang/bottom · error · anyhow::Error

Device I/O error

Error message

Device I/O error: {err:?}

What it means

The DeviceIoControl call with IOCTL_DISK_PERFORMANCE failed inside volume_io, so the DISK_PERFORMANCE counters could not be read from the volume. The handle was successfully opened and closed; only the control-code query failed. This means the OS refused or could not service the disk-performance request for that volume.

Solutions

  1. Enable disk performance counters by running 'diskperf -n' (as Administrator) and retry
  2. Check the wrapped err for the Win32 code — ERROR_INVALID_FUNCTION (1) means the IOCTL/volume combination is unsupported
  3. Skip volumes that do not support the IOCTL and aggregate only the ones that do
  4. Verify the volume is a physical/local disk, not a mapped network or virtual volume

Example fix

// before
if let Err(err) = ret {
    bail!("Device I/O error: {err:?}");
}
// after
if let Err(err) = ret {
    if err.code() == HRESULT::from_win32(ERROR_INVALID_FUNCTION) {
        return Ok(DISK_PERFORMANCE::default()); // counters unsupported/disabled
    }
    bail!("Device I/O error: {err:?}");
}
Defensive patterns

Strategy: fallback

Validate before calling

// enable counters beforehand (requires admin):
// run `diskperf -n` once on the target machine

Try / catch

match volume_io(&volume) {
    Ok(stats) => Some(stats),
    Err(e) if e.to_string().contains("Device I/O error") => None, // unsupported
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: DeviceIoControl returns Err — most commonly ERROR_INVALID_FUNCTION because disk performance counters are disabled on the system, or the output buffer/size was rejected, or the volume does not support the IOCTL.

Common situations: Windows systems where 'diskperf -n' was never enabled (counters off by default on some configs/skylines); virtualized or network volumes that do not implement IOCTL_DISK_PERFORMANCE; collecting stats on dynamic/foreign disks.

Related errors


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

Appendix: source

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

            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.
    // SAFETY: This should be safe, we will check the result as well.
    let res = unsafe { FindVolumeClose(handle) };
    Ok(res?)
}

View on GitHub (pinned to b77d317502)