astrid-runtime/astrid · error

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

Error message

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

What it means

While enumerating the principal home root, preflight_legacy_audit_sources requires every entry to be a regular directory (no symlinks, files, or special files) before treating it as a principal home. A symlinked or non-directory entry could redirect the importer into an unintended subtree, so the scan aborts with InvalidData naming the offending entry.

Source

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

                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() {
            return Err(io::Error::new(
                io::ErrorKind::InvalidData,
                format!(
                    "legacy principal-home entry is not a regular directory: {}",
                    principal_root.display()
                ),
            ));
        }
        if device_id(&principal_metadata) != root_device {
            return Err(io::Error::new(
                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)

Solutions

  1. Inspect the reported entry (ls -la) and replace the symlink/file with a real directory if it should be a principal home.
  2. Move unrelated files, sockets, or symlinks out of the home root so it contains only directories.
  3. If the entry is a symlink to real data, replace it with the actual directory (copy or move the target).
  4. Re-run migrate_legacy_audit after the root contains only regular directories.

Example fix

// before
lrwxrwxrwx principal-7f3a -> /mnt/data/principal-7f3a
// after
$ rm principal-7f3a && mv /mnt/data/principal-7f3a . && chmod 700 principal-7f3a
Defensive patterns

Strategy: validation

Validate before calling

fn scan_home_root(root: &std::path::Path) -> std::io::Result<Vec<std::path::PathBuf>> {
    let mut bad = Vec::new();
    for e in std::fs::read_dir(root)? {
        let p = e?.path();
        let m = std::fs::symlink_metadata(&p)?;
        if m.file_type().is_symlink() || !m.is_dir() {
            bad.push(p);
        }
    }
    Ok(bad) // must be empty before migrating
}

Type guard

fn entry_is_plain_dir(p: &std::path::Path) -> bool {
    std::fs::symlink_metadata(p).map(|m| !m.file_type().is_symlink() && m.is_dir()).unwrap_or(false)
}

Try / catch

match result {
    Err(e) if e.kind() == std::io::ErrorKind::InvalidData && e.to_string().contains("not a regular directory") => {
        // clean non-directory entries out of the home root, then retry
    }
    other => other?,
}

Prevention

When it happens

Trigger: migrate_legacy_audit finds any entry directly under the home root where symlink_metadata reports is_symlink() || !is_dir() (host_fs.rs:380) — e.g. a symlinked principal directory, a stray file, a socket or device node placed in the home root.

Common situations: Symlinking a per-principal directory to another disk; temporary files or sockets dropped into the home root; a partially-created principal directory replaced by a placeholder file.

Understand the failure class

Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.

Related errors


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