{"record":{"id":"c3f923e17e476767","repo":"unicity-aos/aos-ce","slug":"bundled-product-runtime-path-must-have-a-final-component","errorCode":null,"errorMessage":"bundled product runtime path must have a final component","messagePattern":"bundled product runtime path must have a final component","errorType":"validation","errorClass":"io::Error","httpStatus":null,"severity":"error","filePath":"crates/unicity-aos-bootstrap/src/migration.rs","lineNumber":548,"sourceCode":"}\n\nfn checked_target_path(path: &Path) -> io::Result<PathBuf> {\n    match fs::symlink_metadata(path) {\n        Ok(metadata) => {\n            if metadata.file_type().is_symlink() || !metadata.is_dir() {\n                return invalid(\"bundled product runtime must be a real directory\");\n            }\n            path.canonicalize()\n        }\n        Err(error) if error.kind() == io::ErrorKind::NotFound => {\n            let parent = path.parent().ok_or_else(|| {\n                io::Error::new(\n                    io::ErrorKind::InvalidInput,\n                    \"bundled product runtime path must have a parent\",\n                )\n            })?;\n            let name = path.file_name().ok_or_else(|| {\n                io::Error::new(\n                    io::ErrorKind::InvalidInput,\n                    \"bundled product runtime path must have a final component\",\n                )\n            })?;\n            Ok(parent.canonicalize()?.join(name))\n        }\n        Err(error) => Err(error),\n    }\n}\n\npub(crate) fn imported_legacy_distros(home: &AosHome) -> io::Result<Vec<LegacyDistro>> {\n    let receipt = read_receipt(&home.migration_receipt())?;\n    Ok(receipt.legacy_distros)\n}\n\nfn legacy_distros(runtime_home: &Path) -> io::Result<Vec<LegacyDistro>> {\n    let homes = runtime_home.join(\"home\");\n    if !homes.is_dir() {","sourceCodeStart":530,"sourceCodeEnd":566,"githubUrl":"https://github.com/unicity-aos/aos-ce/blob/f6f22024fb1e8d122f28a1b4a9f75aee448ae839/crates/unicity-aos-bootstrap/src/migration.rs#L530-L566","documentation":"In checked_target_path's NotFound branch, after obtaining the parent, the code calls file_name() to recover the final path component; if the path ends in \"..\" or is a bare root, file_name() returns None and this InvalidInput error is thrown. It prevents canonicalizing a target whose final component cannot be reattached.","triggerScenarios":"Calling migrate_runtime with a bundled product runtime target that has a parent but no final component, such as \"/opt/product/..\", \"/\", or a path ending in a trailing traversal component.","commonSituations":"Building the target by string concatenation that leaves a trailing slash-dot segment; normalizing away the runtime directory name before calling the API.","solutions":["Pass a target path whose final component is the runtime directory name (e.g. /opt/product/runtime, not /opt/product/..)","Strip trailing slashes and resolve any \"..\" components before calling migrate_runtime","Assert path.file_name().is_some() on the constructed target before invoking the migration"],"exampleFix":"// before\nlet target = Path::new(\"/opt/product/..\");\nmigrate_runtime(target, ...)?;\n\n// after\nlet target = Path::new(\"/opt/product/runtime\");\nmigrate_runtime(target, ...)?;","handlingStrategy":"validation","validationCode":"fn target_final_component_ok(target: &std::path::Path) -> bool {\n    target.file_name().is_some()\n}","typeGuard":"fn final_component(p: &std::path::Path) -> Option<std::ffi::OsString> {\n    p.file_name().map(|n| n.to_os_string())\n}","tryCatchPattern":"match migrate_runtime(&target, ...) {\n    Ok(()) => {},\n    Err(e) if e.to_string().contains(\"must have a final component\") =>\n        eprintln!(\"target must end with the runtime directory name, not '..' or '/'\"),\n    Err(e) => return Err(e),\n}","preventionTips":["Normalize targets (strip trailing slashes and resolve '..') before invoking migration","Ensure the final path component is the runtime directory name itself","Add a pre-flight assert on path.file_name() in migration wrappers"],"tags":["rust","path","migration"],"backgroundTag":"invalid-argument-value","analyzedSha":"f6f22024fb1e8d122f28a1b4a9f75aee448ae839","analyzedAt":"2026-09-13T03:04:44.565Z","contentChangedAt":"2026-09-13T03:04:44.565Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}