unicity-aos/aos-ce · error · std::io::Error

bundled executable must have a parent directory

Error message

bundled executable must have a parent directory

What it means

runtime_executable_command calls Path::parent() on the bundled executable path and, since parent() returns None only for paths with no parent component (e.g. a bare relative name resolved oddly or a root-less path), maps that to InvalidInput "bundled executable must have a parent directory". The parent is required to build the child process PATH that puts bundled sibling tools first.

Solutions

  1. Pass the full absolute path to the executable file (e.g. /opt/runtime/bin/unicityd), never a directory root or empty path.
  2. Resolve the executable with canonicalize() before invoking so the path always has a parent.
  3. Validate the override path points to an existing file before building the Command.

Example fix

// before
let exe = Path::new("");
let cmd = runtime.runtime_executable_command(exe, args)?;
// after
let exe = PathBuf::from("/opt/unicity/bin/unicityd");
debug_assert!(exe.is_file());
let cmd = runtime.runtime_executable_command(&exe, args)?;
Defensive patterns

Strategy: validation

Validate before calling

fn require_parented_file(exe: &Path) -> Result<(), String> {
    if exe.parent().is_none() {
        return Err(format!("executable path {:?} has no parent; use a full file path", exe));
    }
    if !exe.is_file() {
        return Err(format!("{:?} is not an existing file", exe));
    }
    Ok(())
}

Type guard

fn is_full_executable_path(exe: &Path) -> bool {
    exe.parent().is_some() && exe.is_file()
}

Try / catch

match runtime.runtime_command_with_args(&exe, args) {
    Err(e) if e.to_string().contains("must have a parent directory") => {
        eprintln!("override executable must be a full file path, got {:?}", exe);
    }
    other => other?,
}

Prevention

When it happens

Trigger: Calling runtime_executable_command (via runtime_command_with_args or foreground_daemon_command) with an executable Path whose parent() is None — practically only paths like "/" or pathological inputs that yield no parent component.

Common situations: A misconfigured override pointing the executable at a filesystem root or empty/degenerate path; constructing the path programmatically and accidentally passing "" or "/" instead of the binary file path.

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


AI-assisted analysis of unicity-aos/aos-ce@f6f22024fb (2026-09-13). Data as JSON: /api/errors/3b96306808fe7dfd. Report an issue: GitHub.

Appendix: source

Thrown at crates/unicity-aos-bootstrap/src/lib.rs:359

    /// # Errors
    /// Returns an error when the release runtime bin or inherited host PATH
    /// cannot be represented safely as a child PATH.
    pub fn runtime_command_with_args<I, S>(&self, args: I) -> io::Result<Command>
    where
        I: IntoIterator<Item = S>,
        S: AsRef<OsStr>,
    {
        let runtime_binary = self.runtime_binary();
        self.runtime_executable_command(&runtime_binary, args)
    }

    fn runtime_executable_command<I, S>(&self, executable: &Path, args: I) -> io::Result<Command>
    where
        I: IntoIterator<Item = S>,
        S: AsRef<OsStr>,
    {
        let executable_parent = executable.parent().ok_or_else(|| {
            io::Error::new(
                io::ErrorKind::InvalidInput,
                "bundled executable must have a parent directory",
            )
        })?;
        // An explicit absolute package override points at a complete runtime
        // bundle, so keep its sibling tools ahead of the host PATH as well.
        let path_prefix = match std::env::var_os("UNICITY_AOS_RUNTIME_BIN").map(PathBuf::from) {
            Some(path) if path.is_absolute() => path
                .parent()
                .map(Path::to_path_buf)
                .unwrap_or_else(|| executable_parent.to_path_buf()),
            _ => self.release_runtime_bin_dir(),
        };
        let mut command = Command::new(executable);
        command
            .env("ASTRID_HOME", self.runtime_home())
            .env("ASTRID_WORKSPACE_STATE_DIR", AOS_WORKSPACE_STATE_DIR)
            .env("ASTRID_ENFORCED_DISTRO", self.unicity_ce_manifest_path())

View on GitHub (pinned to f6f22024fb)