rustfs/rustfs · error · std::io::Error
rename destination contains an invalid path component
Error message
rename destination contains an invalid path component
What it means
After stripping base_dir, the relative destination path contains a component that is not Normal or CurDir — typically ParentDir (`..`), or a root/prefix component leaking in. The guarded create only walks plain named components, so it rejects the path with InvalidInput before touching the filesystem.
Solutions
- Lexically clean the relative path (drop ParentDir components) before calling
- Validate user input at the API boundary so traversal segments never reach the disk layer
- Unit-test the path builder with dot-dot, dot, and empty components
Example fix
// before
let dir = base.join(format!("{}/../tmp", rel));
// after
let dir = base.join(rel).join("tmp"); Defensive patterns
Strategy: validation
Validate before calling
fn is_safe_relative(rel: &std::path::Path) -> bool {
rel.components()
.all(|c| matches!(c, std::path::Component::Normal(_) | std::path::Component::CurDir))
} Try / catch
Err(err) if err.kind() == io::ErrorKind::InvalidInput && err.to_string().contains("invalid path component") => reject the input as a traversal attempt; no retry Prevention
- Validate user-supplied names at the API boundary (no dot-dot, no separators)
- Clean relative paths before joining onto the base
When it happens
Trigger: dir_path like <base>/../escape strips to ../escape; paths assembled from unnormalized user input containing dot-dot segments.
Common situations: User-supplied object or bucket names containing traversal segments after a buggy join; URL-decoded prefixes reintroducing dot-dot.
Related errors
- rename base directory contains an invalid path component
- guarded destination path contains an invalid component
- rename destination must remain below its base directory
- rename destination parent must have a name
- Windows guarded path contains an invalid component
AI-assisted analysis of rustfs/rustfs@201c653dcd (2026-08-20).
Data as JSON: /api/errors/7308d47dc363cee0.
Report an issue: GitHub.
Appendix: source
Thrown at crates/ecstore/src/disk/os.rs:3742
#[cfg(windows)]
fn windows_rename_source_is_allowed(attributes: u32, reparse_tag: u32) -> bool {
use windows_sys::Win32::{Storage::FileSystem::FILE_ATTRIBUTE_REPARSE_POINT, System::SystemServices::IO_REPARSE_TAG_DEDUP};
attributes & FILE_ATTRIBUTE_REPARSE_POINT == 0 || reparse_tag == IO_REPARSE_TAG_DEDUP
}
pub(crate) fn mkdir_all_below_existing_base_std(
dir_path: &Path,
base_dir: &Path,
publication_root: &PublicationRoot,
) -> io::Result<ExistingBaseDirectoryGuard> {
let relative = dir_path
.strip_prefix(base_dir)
.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "rename destination must remain below its base directory"))?;
for component in relative.components() {
if !matches!(component, Component::Normal(_) | Component::CurDir) {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"rename destination contains an invalid path component",
));
}
}
#[cfg(unix)]
{
let _ = publication_root;
use rustix::fs::{Mode, OFlags, mkdirat, open, openat};
use rustix::io::Errno;
let flags = OFlags::RDONLY | OFlags::DIRECTORY | OFlags::NOFOLLOW | OFlags::CLOEXEC;
let mode = Mode::RWXU | Mode::RWXG | Mode::RWXO;
let mut parents = vec![open(base_dir, flags, Mode::empty()).map_err(io::Error::from)?];
for component in relative.components() {
let Component::Normal(component) = component else {View on GitHub (pinned to 201c653dcd)