{"record":{"id":"cecad56b80488bc7","repo":"rustfs/rustfs","slug":"invalidinput-cecad5","errorCode":"InvalidInput","errorMessage":"missing object metadata parent","messagePattern":"missing object metadata parent","errorType":"exception","errorClass":"std::io::Error","httpStatus":null,"severity":"error","filePath":"crates/ecstore/src/disk/local.rs","lineNumber":187,"sourceCode":"        Ok(metadata) if metadata.is_file() => {\n            remove_file_if_exists(restore)?;\n            std::fs::hard_link(backup, restore)?;\n            std::fs::rename(restore, current)\n        }\n        Ok(_) => Err(std::io::Error::new(ErrorKind::InvalidData, \"multipart transaction backup is not a file\")),\n        Err(err) if err.kind() == ErrorKind::NotFound && absent.is_file() => remove_file_if_exists(current),\n        Err(err) => Err(err),\n    }\n}\n\nfn rollback_committed_rename_std(\n    dst_file_path: &Path,\n    new_data_path: Option<&Path>,\n    rollback_data_dir: Option<Uuid>,\n) -> std::io::Result<()> {\n    if let Some(old_data_dir) = rollback_data_dir {\n        let Some(dst_parent) = dst_file_path.parent() else {\n            return Err(std::io::Error::new(ErrorKind::InvalidInput, \"missing object metadata parent\"));\n        };\n        let backup_path = dst_parent.join(old_data_dir.to_string()).join(STORAGE_FORMAT_FILE_BACKUP);\n        std::fs::rename(backup_path, dst_file_path)?;\n    } else {\n        remove_file_if_exists(dst_file_path)?;\n    }\n\n    if let Some(new_data_path) = new_data_path {\n        remove_dir_all_if_exists(new_data_path)?;\n    }\n\n    Ok(())\n}\n\nfn rollback_inline_metadata_commit_std(\n    dst_file_path: &Path,\n    rollback_data_dir: Option<Uuid>,\n    local_rollback_path: Option<&Path>,","sourceCodeStart":169,"sourceCodeEnd":205,"githubUrl":"https://github.com/rustfs/rustfs/blob/9e6e02ea09c86bedf44c7bd64a74ea02a0cff1de/crates/ecstore/src/disk/local.rs#L169-L205","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nlet path = build_meta_path(bucket, object); // can yield bare xl.meta when bucket is empty\n// after\nif bucket.is_empty() || object.is_empty() {\n    return Err(invalid_argument(\"bucket and object must be non-empty\"));\n}\nlet path = build_meta_path(bucket, object);\ndebug_assert!(path.parent().is_some(), \"metadata path must have a parent\");","handlingStrategy":"validation","validationCode":"fn ensure_metadata_path(volume: &str, bucket: &str, path: &Path) -> std::io::Result<()> {\n    if volume.is_empty() || bucket.is_empty() || path.parent().is_none() {\n        return Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, \"metadata path must live under bucket/object directories\"));\n    }\n    Ok(())\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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"],"tags":["rust","filesystem","path-construction","rollback","invalid-input"],"backgroundTag":"invalid-path-construction","analyzedSha":"9e6e02ea09c86bedf44c7bd64a74ea02a0cff1de","analyzedAt":"2026-08-16T20:34:17.560Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}