astrid-runtime/astrid · error

legacy principal-home entry is not a regular directory

Error message

legacy principal-home entry is not a regular directory: {principal_root}

What it means

During the legacy principal-home audit, each entry directly under the home root must be a regular directory (checked with `symlink_metadata`, so symlinks are rejected even if they point to directories). An entry such as a per-principal directory that is a symlink, file, or other non-directory type triggers this InvalidData error, after `verify_no_redirects` already cleared the root path itself.

Solutions

  1. Replace each offending symlink with a real directory (move the data back or use a bind mount instead of a symlink).
  2. Remove or relocate stray files that occupy principal-directory names.
  3. Exclude/fix sync tools (Dropbox, stow, dotbot) that convert directories into symlinks.
  4. Re-run `verify_no_redirects`/the audit after entries are plain directories.

Example fix

// before: principal dir is a symlink
~/.astrid/principals/alice -> /mnt/secrets/alice
// after: real directory
rm ~/.astrid/principals/alice
mkdir ~/.astrid/principals/alice && cp -a /mnt/secrets/alice/. ~/.astrid/principals/alice/
Defensive patterns

Strategy: validation

Validate before calling

for entry in std::fs::read_dir(home.home_dir())? {
    let p = entry?.path();
    let md = std::fs::symlink_metadata(&p)?;
    if md.file_type().is_symlink() || !md.is_dir() {
        eprintln!("{} must be a plain directory", p.display());
    }
}

Type guard

fn entries_are_plain_dirs(root: &std::path::Path) -> std::io::Result<bool> {
    Ok(std::fs::read_dir(root)?.try_all(|e| {
        let md = std::fs::symlink_metadata(e.path())?;
        Ok(!md.file_type().is_symlink() && md.is_dir())
    })?)
}

Try / catch

match run_legacy_home_audit(&home) {
    Err(e) if e.to_string().contains("entry is not a regular directory") => {
        eprintln!("a home entry is a symlink/file; materialize it as a real directory and retry");
    }
    other => other?,
}

Prevention

When it happens

Trigger: Running the audit when any entry under the home root (e.g. a principal's directory) is a symlink or a regular file instead of a directory — detected via `symlink_metadata(...).file_type().is_symlink() || !is_dir()`.

Common situations: Dotfile/sync managers symlinking principal directories to a data volume; a corrupt or partial restore leaving a file where a principal directory should be; manually moving principal data to another disk with a symlink left behind.

Related errors


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

Appendix: source

Thrown at crates/astrid-kernel/src/lib.rs:4143

                format!(
                    "legacy principal-home root is not a directory: {}",
                    root.display()
                ),
            ));
        },
        Ok(metadata) => metadata,
        Err(error) if error.kind() == std::io::ErrorKind::NotFound => return Ok(false),
        Err(error) => return Err(error),
    };
    let root_device = audit_tree_device(&metadata);
    let mut default_source_present = false;
    astrid_core::platform_fs::verify_no_redirects(&root)?;
    for entry in std::fs::read_dir(&root)? {
        let entry = entry?;
        let principal_root = entry.path();
        let principal_metadata = std::fs::symlink_metadata(&principal_root)?;
        if principal_metadata.file_type().is_symlink() || !principal_metadata.is_dir() {
            return Err(std::io::Error::new(
                std::io::ErrorKind::InvalidData,
                format!(
                    "legacy principal-home entry is not a regular directory: {}",
                    principal_root.display()
                ),
            ));
        }
        if audit_tree_device(&principal_metadata) != root_device {
            return Err(std::io::Error::new(
                std::io::ErrorKind::InvalidData,
                format!(
                    "legacy principal-home entry crosses a filesystem boundary: {}",
                    principal_root.display()
                ),
            ));
        }
        astrid_core::platform_fs::verify_no_redirects(&principal_root)?;
        let local_root = principal_root.join(".local");

View on GitHub (pinned to affd8760f4)