{"record":{"id":"ece0f20d1c8cef44","repo":"unicity-aos/aos-ce","slug":"aos-managed-path-must-be-a-real-directory","errorCode":null,"errorMessage":"AOS managed path must be a real directory: {}","messagePattern":"AOS managed path must be a real directory: (.+?)","errorType":"validation","errorClass":"std::io::Error","httpStatus":null,"severity":"error","filePath":"crates/unicity-aos-bootstrap/src/lib.rs","lineNumber":561,"sourceCode":"            if metadata.permissions().mode() & 0o111 == 0 {\n                return Err(io::Error::new(\n                    io::ErrorKind::PermissionDenied,\n                    format!(\n                        \"bundled {label} executable is not executable at {}\",\n                        binary.display()\n                    ),\n                ));\n            }\n        }\n        Ok(())\n    }\n}\n\nfn create_private_dir(path: &Path) -> io::Result<()> {\n    fs::create_dir_all(path)?;\n    let metadata = fs::symlink_metadata(path)?;\n    if metadata.file_type().is_symlink() || !metadata.is_dir() {\n        return Err(io::Error::new(\n            io::ErrorKind::InvalidInput,\n            format!(\n                \"AOS managed path must be a real directory: {}\",\n                path.display()\n            ),\n        ));\n    }\n    #[cfg(unix)]\n    {\n        use std::os::unix::fs::PermissionsExt;\n        fs::set_permissions(path, fs::Permissions::from_mode(0o700))?;\n    }\n    Ok(())\n}\n\nfn validate_path_entry(path: &Path, variable: &str) -> io::Result<()> {\n    std::env::join_paths(std::iter::once(path))\n        .map(drop)","sourceCodeStart":543,"sourceCodeEnd":579,"githubUrl":"https://github.com/unicity-aos/aos-ce/blob/f6f22024fb1e8d122f28a1b4a9f75aee448ae839/crates/unicity-aos-bootstrap/src/lib.rs#L543-L579","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","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)."],"exampleFix":"# before: symlinked managed directory\n~/.aos -> /mnt/data/aos\n# after: real directory via env relocation\nrm ~/.aos\nexport AOS_HOME=/mnt/data/aos   # library creates a real dir here","handlingStrategy":"validation","validationCode":"use std::fs;\nlet md = fs::symlink_metadata(&path)?;\nif md.file_type().is_symlink() || !md.is_dir() {\n    return Err(format!(\"{} must be a real directory (not a symlink or file); remove/move it and let AOS recreate it\", path.display()));\n}","typeGuard":null,"tryCatchPattern":"match result {\n    Err(e) if e.kind() == std::io::ErrorKind::InvalidInput && e.to_string().contains(\"must be a real directory\") => {\n        eprintln!(\"{} is a symlink or file; replace it with a real directory or relocate via AOS_HOME\", path.display());\n    }\n    other => other?,\n}","preventionTips":["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."],"tags":["filesystem","symlink","validation","rust"],"backgroundTag":"path-is-not-a-directory","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"}