astrid-runtime/astrid · error

legacy audit source is not a regular directory

Error message

legacy audit source is not a regular directory: {source}

What it means

The retirement routine requires the audit source to be a real directory. It uses symlink_metadata so symlinks are detected directly: a symlink to a directory, or any non-directory entry (file, fifo, etc.), is rejected with InvalidData because renaming a link would not move the actual audit data safely.

Solutions

  1. Replace the symlink/file at the source path with a real directory containing the audit data, then retry
  2. If it is a symlink, resolve it (mv the target into place) so the migration operates on real data
  3. Inspect with `ls -la <source>` and `stat <source>` to determine what the entry is

Example fix

// before
audit -> /var/lib/app/audit   (symlink)
// after
rm audit && mv /var/lib/app/audit ./audit   (real directory)
Defensive patterns

Strategy: validation

Validate before calling

fn source_is_real_dir(p: &std::path::Path) -> bool {
    std::fs::symlink_metadata(p)
        .map(|m| !m.file_type().is_symlink() && m.is_dir())
        .unwrap_or(false)
}
// if !source_is_real_dir(&source) { resolve manually first }

Type guard

fn is_real_directory(p: &std::path::Path) -> bool {
    matches!(std::fs::symlink_metadata(p), Ok(m) if m.is_dir() && !m.file_type().is_symlink())
}

Try / catch

if let Err(e) = retire_legacy_audit(&home, &source) {
    if e.kind() == std::io::ErrorKind::InvalidData
        && e.to_string().contains("not a regular directory") {
        // materialize the symlink/target into a real dir, then retry
    } else { return Err(e.into()); }
}

Prevention

When it happens

Trigger: Running legacy audit retirement when the source audit path is a symlink to another directory, a regular file, or otherwise not a directory.

Common situations: Audit dir relocated via symlink by an admin; a leftover file occupying the audit path after a failed cleanup; tmpfs-to-disk symlink tricks in containers.

Related errors


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

Appendix: source

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

        .principal_home(&astrid_core::PrincipalId::default())
        .audit_dir();
    if source != expected {
        return Err(std::io::Error::new(
            std::io::ErrorKind::InvalidInput,
            "legacy audit retirement source is outside the default principal audit path",
        ));
    }
    astrid_core::platform_fs::verify_no_redirects(source.parent().ok_or_else(|| {
        std::io::Error::new(
            std::io::ErrorKind::InvalidInput,
            "legacy audit source has no parent",
        )
    })?)?;
    astrid_core::platform_fs::ensure_private_directory(&home.migrations_dir())?;
    astrid_core::platform_fs::verify_no_redirects(&home.migrations_dir())?;
    match std::fs::symlink_metadata(source) {
        Ok(metadata) if metadata.file_type().is_symlink() || !metadata.is_dir() => {
            return Err(std::io::Error::new(
                std::io::ErrorKind::InvalidData,
                format!(
                    "legacy audit source is not a regular directory: {}",
                    source.display()
                ),
            ));
        },
        Ok(_) => {
            if std::fs::symlink_metadata(&retired).is_ok() {
                return Err(std::io::Error::new(
                    std::io::ErrorKind::AlreadyExists,
                    format!(
                        "legacy audit retirement is ambiguous: {}",
                        retired.display()
                    ),
                ));
            }
            let root_device = audit_tree_device(&std::fs::symlink_metadata(source)?);

View on GitHub (pinned to affd8760f4)