{"record":{"id":"ba94efe0bf4d0c7a","repo":"unicity-aos/aos-ce","slug":"aos-capsule-directory-must-be-a-real-directory","errorCode":null,"errorMessage":"AOS capsule directory must be a real directory: {}","messagePattern":"AOS capsule directory must be a real directory: (.+?)","errorType":"validation","errorClass":"io::Error","httpStatus":null,"severity":"error","filePath":"crates/unicity-aos-bootstrap/src/lib.rs","lineNumber":725,"sourceCode":"            ));\n        }\n        assets.push(asset);\n    }\n    Ok(assets)\n}\n\nfn validate_capsule_dir(path: &Path, expected: &[String]) -> io::Result<PathBuf> {\n    let metadata = fs::symlink_metadata(path).map_err(|error| {\n        io::Error::new(\n            error.kind(),\n            format!(\n                \"AOS capsule directory is unavailable at {}: {error}\",\n                path.display()\n            ),\n        )\n    })?;\n    if metadata.file_type().is_symlink() || !metadata.is_dir() {\n        return Err(io::Error::new(\n            io::ErrorKind::InvalidInput,\n            format!(\n                \"AOS capsule directory must be a real directory: {}\",\n                path.display()\n            ),\n        ));\n    }\n    let canonical = path.canonicalize()?;\n    let mut actual = Vec::new();\n    for entry in fs::read_dir(&canonical)? {\n        let entry = entry?;\n        let metadata = fs::symlink_metadata(entry.path())?;\n        if metadata.file_type().is_symlink() || !metadata.is_file() {\n            return Err(io::Error::new(\n                io::ErrorKind::InvalidInput,\n                format!(\n                    \"AOS capsule directory contains a non-regular entry: {}\",\n                    entry.path().display()","sourceCodeStart":707,"sourceCodeEnd":743,"githubUrl":"https://github.com/unicity-aos/aos-ce/blob/f6f22024fb1e8d122f28a1b4a9f75aee448ae839/crates/unicity-aos-bootstrap/src/lib.rs#L707-L743","documentation":"Thrown by validate_capsule_dir when the capsule path exists but is a symbolic link or not a directory. The library deliberately rejects symlinks so the capsule directory cannot be redirected elsewhere (an integrity/security measure), and requires a real directory to enumerate.","triggerScenarios":"Pointing capsule_dir_with at a symlink to the real directory; pointing it at a regular file or a mount point reported as non-dir; platform-specific filesystem entries where symlink_metadata reports a link.","commonSituations":"Users symlink ~/.aos/capsules to another disk to save space; environment variables or config pointing at a file instead of a directory; tmpfs/bind-mount setups where the path resolves oddly.","solutions":["Replace the symlink with a real directory (e.g. copy or move the contents back, or bind-mount instead of symlink)","Ensure the configured path is the actual directory, not a file or link","If redirection is needed, change the configured path itself rather than symlinking"],"exampleFix":"// before\nln -s /data/aos/capsules ~/.aos/capsules\n// after\nrm ~/.aos/capsules && mkdir ~/.aos/capsules && cp -a /data/aos/capsules/. ~/.aos/capsules/","handlingStrategy":"validation","validationCode":"fn is_real_directory(path: &std::path::Path) -> bool {\n    match std::fs::symlink_metadata(path) {\n        Ok(md) => !md.file_type().is_symlink() && md.is_dir(),\n        Err(_) => false,\n    }\n}","typeGuard":"fn is_real_dir(md: &std::fs::Metadata) -> bool {\n    !md.file_type().is_symlink() && md.is_dir()\n}","tryCatchPattern":"match bootstrap::capsule_dir_with(&home) {\n    Err(e) if e.to_string().contains(\"must be a real directory\") => {\n        eprintln!(\"Replace the symlink/file at the capsule path with a real directory\");\n    }\n    Err(e) => return Err(e),\n    Ok(dir) => dir,\n}","preventionTips":["Never symlink the capsule directory to another location; configure the real path instead","Check symlink_metadata before pointing the library at a path","Document that the capsule path must be a physical directory"],"tags":["rust","filesystem","symlink","security","capsules"],"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"}