unicity-aos/aos-ce · error · io::Error
bundled product runtime path must have a final component
Error message
bundled product runtime path must have a final component
What it means
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.
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
Example fix
// before
let target = Path::new("/opt/product/..");
migrate_runtime(target, ...)?;
// after
let target = Path::new("/opt/product/runtime");
migrate_runtime(target, ...)?; Defensive patterns
Strategy: validation
Validate before calling
fn target_final_component_ok(target: &std::path::Path) -> bool {
target.file_name().is_some()
} Type guard
fn final_component(p: &std::path::Path) -> Option<std::ffi::OsString> {
p.file_name().map(|n| n.to_os_string())
} Try / catch
match migrate_runtime(&target, ...) {
Ok(()) => {},
Err(e) if e.to_string().contains("must have a final component") =>
eprintln!("target must end with the runtime directory name, not '..' or '/'"),
Err(e) => return Err(e),
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: Building the target by string concatenation that leaves a trailing slash-dot segment; normalizing away the runtime directory name before calling the API.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- bundled product runtime path must have a parent
- must be an absolute path
- bundled executable must have a parent directory
- cannot construct the bundled runtime PATH
- cannot contain a platform PATH separator
AI-assisted analysis of unicity-aos/aos-ce@f6f22024fb (2026-09-13).
Data as JSON: /api/errors/c3f923e17e476767.
Report an issue: GitHub.
Appendix: source
Thrown at crates/unicity-aos-bootstrap/src/migration.rs:548
}
fn checked_target_path(path: &Path) -> io::Result<PathBuf> {
match fs::symlink_metadata(path) {
Ok(metadata) => {
if metadata.file_type().is_symlink() || !metadata.is_dir() {
return invalid("bundled product runtime must be a real directory");
}
path.canonicalize()
}
Err(error) if error.kind() == io::ErrorKind::NotFound => {
let parent = path.parent().ok_or_else(|| {
io::Error::new(
io::ErrorKind::InvalidInput,
"bundled product runtime path must have a parent",
)
})?;
let name = path.file_name().ok_or_else(|| {
io::Error::new(
io::ErrorKind::InvalidInput,
"bundled product runtime path must have a final component",
)
})?;
Ok(parent.canonicalize()?.join(name))
}
Err(error) => Err(error),
}
}
pub(crate) fn imported_legacy_distros(home: &AosHome) -> io::Result<Vec<LegacyDistro>> {
let receipt = read_receipt(&home.migration_receipt())?;
Ok(receipt.legacy_distros)
}
fn legacy_distros(runtime_home: &Path) -> io::Result<Vec<LegacyDistro>> {
let homes = runtime_home.join("home");
if !homes.is_dir() {View on GitHub (pinned to f6f22024fb)