ClementTsang/bottom · error · anyhow::Error

Error while iterating over volumes

Error message

Error while iterating over volumes: {err:?}

What it means

While walking volumes with FindNextVolumeW in all_volume_io, the last OS error was neither success nor ERROR_NO_MORE_FILES, so the library closes the find handle and bails with the iteration error. ERROR_NO_MORE_FILES is the normal loop-exit condition; any other code means enumeration genuinely failed midway. Results gathered so far are discarded.

Solutions

  1. Inspect the wrapped err's Win32 code; ERROR_MORE_DATA means the path buffer (MAX_PATH u16s) is too small — use a larger buffer
  2. Retry the full enumeration; transient races from volumes appearing/disappearing resolve on a second pass
  3. Return the partially collected results with a warning instead of bailing
  4. Check disk/storage health — persistent non-NO_MORE_FILES codes can indicate mount manager corruption (chkdsk / mountvol checks)

Example fix

// before
match err.raw_os_error() {
    Some(ERROR_NO_MORE_FILES) => {}
    _ => {
        close_find_handle(handle)?;
        bail!("Error while iterating over volumes: {err:?}");
    }
}
// after
match err.raw_os_error() {
    Some(ERROR_NO_MORE_FILES) => {}
    _ => {
        let _ = close_find_handle(handle);
        eprintln!("partial volume enumeration: {err:?}"); // keep partial results
    }
}
Defensive patterns

Strategy: retry

Validate before calling

// use a buffer larger than MAX_PATH to survive long volume GUID paths
let mut buffer = [0u16; 128]; // > MAX_PATH (260 bytes / 50 chars GUID path is fine, but be generous)

Try / catch

match all_volume_io() {
    Ok(r) => r,
    Err(e) => {
        log::warn!("volume iteration failed, retrying once: {e:?}");
        all_volume_io().unwrap_or_default()
    }
}

Prevention

When it happens

Trigger: FindNextVolumeW stops succeeding and last_os_error is not ERROR_NO_MORE_FILES — e.g. ERROR_MORE_DATA if a volume path exceeds MAX_PATH, handle invalidated mid-iteration, or the mount point database changed while iterating (volume added/removed).

Common situations: Systems with very long volume GUID paths exceeding the MAX_PATH buffer; hot-plugging or unmounting drives during a scan; corrupted mount manager state mid-enumeration.

Related errors


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

Appendix: source

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

        let volume = current_volume(&buffer);
        ret.push(volume_io(&volume).map(|res| (res, volume.to_string_lossy().to_string())));
    }

    // Now iterate until there are no more volumes.
    while unsafe { FindNextVolumeW(handle, &mut buffer) }.is_ok() {
        let volume = current_volume(&buffer);
        ret.push(volume_io(&volume).map(|res| (res, volume.to_string_lossy().to_string())));
    }

    let err = io::Error::last_os_error();
    match err.raw_os_error() {
        Some(ERROR_NO_MORE_FILES) => {
            // Iteration completed successfully, continue on.
        }
        _ => {
            // Some error occurred.
            close_find_handle(handle)?;
            bail!("Error while iterating over volumes: {err:?}");
        }
    }

    close_find_handle(handle)?;

    Ok(ret)
}

/// Returns the volume name from a mount name if possible.
pub(crate) fn volume_name_from_mount(mount: &str) -> anyhow::Result<String> {
    // According to winapi docs 50 is a reasonable length to accommodate the
    // volume path https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getvolumenameforvolumemountpointw
    const VOLUME_MAX_LEN: usize = 50;

    let mount = {
        let mount_path = Path::new(mount);
        let mut wide_path = mount_path.as_os_str().encode_wide().collect::<Vec<_>>();

View on GitHub (pinned to b77d317502)