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

AOS capsule directory must be a real directory

Error message

AOS capsule directory must be a real directory: {}

What it means

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.

Solutions

  1. Replace the symlink with a real directory (e.g. copy or move the contents back, or bind-mount instead of symlink)
  2. Ensure the configured path is the actual directory, not a file or link
  3. If redirection is needed, change the configured path itself rather than symlinking

Example fix

// before
ln -s /data/aos/capsules ~/.aos/capsules
// after
rm ~/.aos/capsules && mkdir ~/.aos/capsules && cp -a /data/aos/capsules/. ~/.aos/capsules/
Defensive patterns

Strategy: validation

Validate before calling

fn is_real_directory(path: &std::path::Path) -> bool {
    match std::fs::symlink_metadata(path) {
        Ok(md) => !md.file_type().is_symlink() && md.is_dir(),
        Err(_) => false,
    }
}

Type guard

fn is_real_dir(md: &std::fs::Metadata) -> bool {
    !md.file_type().is_symlink() && md.is_dir()
}

Try / catch

match bootstrap::capsule_dir_with(&home) {
    Err(e) if e.to_string().contains("must be a real directory") => {
        eprintln!("Replace the symlink/file at the capsule path with a real directory");
    }
    Err(e) => return Err(e),
    Ok(dir) => dir,
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

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/ba94efe0bf4d0c7a. Report an issue: GitHub.

Appendix: source

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

            ));
        }
        assets.push(asset);
    }
    Ok(assets)
}

fn validate_capsule_dir(path: &Path, expected: &[String]) -> io::Result<PathBuf> {
    let metadata = fs::symlink_metadata(path).map_err(|error| {
        io::Error::new(
            error.kind(),
            format!(
                "AOS capsule directory is unavailable at {}: {error}",
                path.display()
            ),
        )
    })?;
    if metadata.file_type().is_symlink() || !metadata.is_dir() {
        return Err(io::Error::new(
            io::ErrorKind::InvalidInput,
            format!(
                "AOS capsule directory must be a real directory: {}",
                path.display()
            ),
        ));
    }
    let canonical = path.canonicalize()?;
    let mut actual = Vec::new();
    for entry in fs::read_dir(&canonical)? {
        let entry = entry?;
        let metadata = fs::symlink_metadata(entry.path())?;
        if metadata.file_type().is_symlink() || !metadata.is_file() {
            return Err(io::Error::new(
                io::ErrorKind::InvalidInput,
                format!(
                    "AOS capsule directory contains a non-regular entry: {}",
                    entry.path().display()

View on GitHub (pinned to f6f22024fb)