rustfs/rustfs · error · std::io::Error
InvalidInput
InvalidInput
Error message
missing object metadata parent
What it means
rollback_committed_rename_std undoes a committed xl.meta rename by moving the backup from the old data-dir back over the destination; it needs dst_file_path.parent() to locate that directory. Path::parent() returned None, meaning the metadata path was a bare relative filename with no directory component - something valid bucket/object-derived absolute paths never produce. This is a defensive InvalidInput guarding path construction.
Source
Thrown at crates/ecstore/src/disk/local.rs:187
Ok(metadata) if metadata.is_file() => {
remove_file_if_exists(restore)?;
std::fs::hard_link(backup, restore)?;
std::fs::rename(restore, current)
}
Ok(_) => Err(std::io::Error::new(ErrorKind::InvalidData, "multipart transaction backup is not a file")),
Err(err) if err.kind() == ErrorKind::NotFound && absent.is_file() => remove_file_if_exists(current),
Err(err) => Err(err),
}
}
fn rollback_committed_rename_std(
dst_file_path: &Path,
new_data_path: Option<&Path>,
rollback_data_dir: Option<Uuid>,
) -> std::io::Result<()> {
if let Some(old_data_dir) = rollback_data_dir {
let Some(dst_parent) = dst_file_path.parent() else {
return Err(std::io::Error::new(ErrorKind::InvalidInput, "missing object metadata parent"));
};
let backup_path = dst_parent.join(old_data_dir.to_string()).join(STORAGE_FORMAT_FILE_BACKUP);
std::fs::rename(backup_path, dst_file_path)?;
} else {
remove_file_if_exists(dst_file_path)?;
}
if let Some(new_data_path) = new_data_path {
remove_dir_all_if_exists(new_data_path)?;
}
Ok(())
}
fn rollback_inline_metadata_commit_std(
dst_file_path: &Path,
rollback_data_dir: Option<Uuid>,
local_rollback_path: Option<&Path>,View on GitHub (pinned to 9e6e02ea09)
Solutions
- Validate bucket and object names are non-empty before calling disk-level rename/rollback APIs
- Fix the path-join logic that produced a parentless relative metadata path
- Log the final constructed path to confirm it is absolute with a parent directory
- Add a unit test covering empty volume/bucket inputs to the path builder
Example fix
// before
let path = build_meta_path(bucket, object); // can yield bare xl.meta when bucket is empty
// after
if bucket.is_empty() || object.is_empty() {
return Err(invalid_argument("bucket and object must be non-empty"));
}
let path = build_meta_path(bucket, object);
debug_assert!(path.parent().is_some(), "metadata path must have a parent"); Defensive patterns
Strategy: validation
Validate before calling
fn ensure_metadata_path(volume: &str, bucket: &str, path: &Path) -> std::io::Result<()> {
if volume.is_empty() || bucket.is_empty() || path.parent().is_none() {
return Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, "metadata path must live under bucket/object directories"));
}
Ok(())
} Prevention
- Validate bucket/volume/object names at the API boundary, not inside disk code
- Keep one path builder and assert its outputs are absolute with parents
- Unit-test path construction with empty components
- Treat InvalidInput from disk rollback paths as a path-builder bug, not a disk fault
When it happens
Trigger: The rollback path is invoked with a metadata path built from empty volume/bucket components so that path joining collapsed to a parentless relative name like 'xl.meta'; or a caller passed a hand-built path in tests.
Common situations: Empty bucket or volume strings reaching the disk layer; a path-join refactor dropping the prefix component; unit tests constructing raw paths instead of using the API path builders.
Related errors
AI-assisted analysis of rustfs/rustfs@9e6e02ea09 (2026-08-16).
Data as JSON: /api/errors/cecad56b80488bc7.
Report an issue: GitHub.