rustfs/rustfs · error · std::io::Error

rename base directory contains an invalid path component

Error message

rename base directory contains an invalid path component

What it means

On the Windows guard-construction path, a component of base_dir relative to the publication root is neither Normal nor CurDir — a ParentDir, root, or prefix component. The handle chain can only be built through real named directories, so the call fails with InvalidInput before opening anything.

Solutions

  1. Normalize base_dir so it is a plain sequence of Normal components under the publication root
  2. Validate config paths at startup and reject dot-dot segments
  3. Log the offending component when the error fires to pinpoint the config source
Defensive patterns

Strategy: validation

Validate before calling

fn base_has_only_normal_components(base: &std::path::Path, root: &std::path::Path) -> bool {
    match base.strip_prefix(root) {
        Ok(rel) => rel.components().all(|c| matches!(c, std::path::Component::Normal(_) | std::path::Component::CurDir)),
        Err(_) => false,
    }
}

Try / catch

Err(err) if err.kind() == io::ErrorKind::InvalidInput && err.to_string().contains("base directory contains an invalid path component") => fail startup or request with the offending base path; configuration fix required

Prevention

When it happens

Trigger: base_dir itself contains dot-dot after being made relative to the publication root, or was built from mixed absolute/relative segments.

Common situations: Config-provided base paths with trailing dot-dot or prefix components (C:) surviving normalization; refactors that changed how the base is derived from the publication root.

Related errors


AI-assisted analysis of rustfs/rustfs@201c653dcd (2026-08-20). Data as JSON: /api/errors/d22c15733b0a2fca. Report an issue: GitHub.

Appendix: source

Thrown at crates/ecstore/src/disk/os.rs:3796

    {
        use windows_sys::Wdk::Storage::FileSystem::{FILE_OPEN, FILE_OPEN_IF};
        use windows_sys::Win32::Storage::FileSystem::{FILE_SHARE_READ, FILE_SHARE_WRITE};

        let base_relative = publication_root.relative_path(base_dir)?;
        let capacity = base_relative
            .components()
            .count()
            .saturating_add(relative.components().count())
            .saturating_add(1);
        let mut handles = Vec::with_capacity(capacity);
        handles.push(publication_root.directory.clone());
        let mut guard = ExistingBaseDirectoryGuard::new(handles);
        for component in base_relative.components() {
            let Component::Normal(component) = component else {
                if matches!(component, Component::CurDir) {
                    continue;
                }
                return Err(io::Error::new(
                    io::ErrorKind::InvalidInput,
                    "rename base directory contains an invalid path component",
                ));
            };
            let parent = guard
                .handles
                .last()
                .ok_or_else(|| io::Error::other("Windows publication root guard is empty"))?;
            let child = open_windows_directory_component(parent, component, FILE_OPEN)?;
            guard.handles.push(child);
        }
        for component in relative.components() {
            let Component::Normal(component) = component else {
                continue;
            };
            let parent = guard
                .handles
                .last()

View on GitHub (pinned to 201c653dcd)