astrid-runtime/astrid · error

legacy principal-home root is not a directory: {}

Error message

legacy principal-home root is not a directory: {}

What it means

preflight_legacy_audit_sources (non-Unix host path) validates that the principal home root is a plain directory before scanning it for legacy audit sources. If symlink_metadata reports the root is a symlink or not a directory, the migration aborts with InvalidData, because following a redirected home root could make the importer read or retire data outside the intended boundary.

Source

Thrown at crates/astrid-kernel/src/legacy_migration_barrier/host_fs.rs:362

                child.display()
            ),
        ));
    }
    fs::remove_file(child).map_err(io::Error::other)
}

/// Validate the released audit-source boundary on every native host.  Unix
/// adds device and mount checks; all hosts retain no-follow, regular-entry,
/// and default-principal-only checks before the audit importer opens a source.
#[cfg(not(unix))]
pub(super) fn preflight_legacy_audit_sources(
    home: &AstridHome,
    default_source: &Path,
) -> io::Result<bool> {
    let root = home.home_dir();
    let metadata = match fs::symlink_metadata(&root) {
        Ok(metadata) if metadata.file_type().is_symlink() || !metadata.is_dir() => {
            return Err(io::Error::new(
                io::ErrorKind::InvalidData,
                format!(
                    "legacy principal-home root is not a directory: {}",
                    root.display()
                ),
            ));
        },
        Ok(metadata) => metadata,
        Err(error) if error.kind() == io::ErrorKind::NotFound => return Ok(false),
        Err(error) => return Err(error),
    };
    let root_device = device_id(&metadata);
    let mut default_source_present = false;
    astrid_core::platform_fs::verify_no_redirects(&root)?;
    for entry in fs::read_dir(&root).map_err(io::Error::other)? {
        let principal_root = entry.map_err(io::Error::other)?.path();
        let principal_metadata = fs::symlink_metadata(&principal_root).map_err(io::Error::other)?;
        if principal_metadata.file_type().is_symlink() || !principal_metadata.is_dir() {

View on GitHub (pinned to affd8760f4)

Solutions

  1. Replace the symlink at the home root with a real directory (mv the target contents back or use a bind mount).
  2. Remove/rename the stray file occupying the home path and recreate it as a directory.
  3. Point the home configuration (env var/config) at the actual directory location instead of a symlink.
  4. Re-run migrate_legacy_audit once home.home_dir() resolves to a physical directory.

Example fix

// before: dotfile-manager symlink
/home/u/.astrid -> /dotfiles/astrid
// after
$ rm /home/u/.astrid && mkdir /home/u/.astrid && cp -a /dotfiles/astrid/. /home/u/.astrid/
Defensive patterns

Strategy: validation

Validate before calling

fn home_root_ok(home_dir: &std::path::Path) -> std::io::Result<bool> {
    match std::fs::symlink_metadata(home_dir) {
        Ok(m) => Ok(!m.file_type().is_symlink() && m.is_dir()),
        Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(true), // tolerated
        Err(e) => Err(e),
    }
}

Type guard

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

Try / catch

match result {
    Err(e) if e.kind() == std::io::ErrorKind::InvalidData && e.to_string().contains("root is not a directory") => {
        // fix the home path (de-symlink / recreate as directory), then retry
    }
    other => other?,
}

Prevention

When it happens

Trigger: Calling migrate_legacy_audit when the principal home directory path (home.home_dir()) is itself a symlink, a regular file, or another non-directory entry (host_fs.rs:361). Note NotFound is tolerated and returns Ok(false), so only existing-but-wrong types trigger this.

Common situations: Users who symlink their home directory to another location (common with dotfile managers); a file accidentally created at the home path; misconfigured ASTRID home env var pointing at a file.

Related errors


AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09). Data as JSON: /api/errors/08f53c5f2db288cc. Report an issue: GitHub.