unicity-aos/aos-ce · error · std::io::Error
AOS managed path must be a real directory
Error message
AOS managed path must be a real directory: {} What it means
create_private_dir calls fs::create_dir_all and then verifies with symlink_metadata that the resulting path is a genuine directory and not a symlink; otherwise it throws this io::Error (InvalidInput). The library enforces this because AOS-managed directories hold private state and a symlinked path could redirect writes outside the managed layout (a symlink-attack/tofu concern).
Solutions
- Remove the symlink or non-directory at the reported path and let the library create a real directory: rm <path> (for a symlink) or mv it away, then retry.
- If the symlink was for relocation, instead set the AOS home env var (e.g. AOS_HOME) to the real directory location rather than symlinking inside the managed tree.
- Migrate any data from the old location into the newly created real directory.
- Re-run the operation and confirm with 'ls -la' that the path is 'd' (directory), not 'l' (link) or '-' (file).
Example fix
# before: symlinked managed directory ~/.aos -> /mnt/data/aos # after: real directory via env relocation rm ~/.aos export AOS_HOME=/mnt/data/aos # library creates a real dir here
Defensive patterns
Strategy: validation
Validate before calling
use std::fs;
let md = fs::symlink_metadata(&path)?;
if md.file_type().is_symlink() || !md.is_dir() {
return Err(format!("{} must be a real directory (not a symlink or file); remove/move it and let AOS recreate it", path.display()));
} Try / catch
match result {
Err(e) if e.kind() == std::io::ErrorKind::InvalidInput && e.to_string().contains("must be a real directory") => {
eprintln!("{} is a symlink or file; replace it with a real directory or relocate via AOS_HOME", path.display());
}
other => other?,
} Prevention
- Never symlink paths inside the AOS-managed tree; relocate storage via the AOS_HOME env var instead.
- Check `ls -la` for 'l' entries in the state directory after restores or migrations.
- Use dedicated restore/migration tooling that recreates real directories rather than manual cp -s / ln -s.
When it happens
Trigger: Calling ensure_unicity_ce_manifest or ensure_layout when the AOS-managed path (e.g. the manifest or layout directory, typically under AOS_HOME) either already exists as a symlink or is a regular file/special file instead of a directory.
Common situations: A user symlinked the state directory (e.g. ~/.aos -> /mnt/data/aos) to relocate storage to another disk; a previous file was left where the directory is expected; a migration or restore tool recreated paths incorrectly.
Related errors
- product manifest path must be a regular file
- temporary product manifest path must be a regular file
- AOS capsule directory must be a real directory
- AOS capsule directory contains a non-regular entry
- failed to validate ephemeral state exclusion
AI-assisted analysis of unicity-aos/aos-ce@f6f22024fb (2026-09-13).
Data as JSON: /api/errors/ece0f20d1c8cef44.
Report an issue: GitHub.
Appendix: source
Thrown at crates/unicity-aos-bootstrap/src/lib.rs:561
if metadata.permissions().mode() & 0o111 == 0 {
return Err(io::Error::new(
io::ErrorKind::PermissionDenied,
format!(
"bundled {label} executable is not executable at {}",
binary.display()
),
));
}
}
Ok(())
}
}
fn create_private_dir(path: &Path) -> io::Result<()> {
fs::create_dir_all(path)?;
let metadata = fs::symlink_metadata(path)?;
if metadata.file_type().is_symlink() || !metadata.is_dir() {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
format!(
"AOS managed path must be a real directory: {}",
path.display()
),
));
}
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
fs::set_permissions(path, fs::Permissions::from_mode(0o700))?;
}
Ok(())
}
fn validate_path_entry(path: &Path, variable: &str) -> io::Result<()> {
std::env::join_paths(std::iter::once(path))
.map(drop)View on GitHub (pinned to f6f22024fb)