astrid-runtime/astrid · error

legacy audit retirement is ambiguous: {}

Error message

legacy audit retirement is ambiguous: {}

What it means

Before renaming the legacy audit directory into the migrations staging area, retire_legacy_audit_dir checks whether the staging target <migrations_dir>/audit-principal-home.retired already exists. If it does, the outcome of a previous run is ambiguous (already retired? partially deleted?), so the library fails with AlreadyExists instead of overwriting data. This makes interrupted migrations resumable via a deliberate decision rather than silent data loss.

Source

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

            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 fs::symlink_metadata(source) {
        Ok(metadata) if metadata.file_type().is_symlink() || !metadata.is_dir() => {
            return Err(io::Error::new(
                io::ErrorKind::InvalidData,
                format!(
                    "legacy audit source is not a regular directory: {}",
                    source.display()
                ),
            ));
        },
        Ok(_) => {
            if fs::symlink_metadata(&retired).is_ok() {
                return Err(io::Error::new(
                    io::ErrorKind::AlreadyExists,
                    format!(
                        "legacy audit retirement is ambiguous: {}",
                        retired.display()
                    ),
                ));
            }
            let root_device = device_id(&fs::symlink_metadata(source)?);
            validate_audit_tree(source, root_device)?;
            astrid_core::platform_fs::rename_with_write_through(source, &retired)?;
            sync_directory(source.parent().ok_or_else(|| {
                io::Error::new(
                    io::ErrorKind::InvalidInput,
                    "legacy audit source has no parent",
                )
            })?)?;
            sync_directory(&home.migrations_dir())?;
            validate_audit_tree(&retired, root_device)?;

View on GitHub (pinned to affd8760f4)

Solutions

  1. Re-run migrate_legacy_audit: the NotFound branch resumes cleanup of an existing .retired staging dir when the source is gone.
  2. If the source still exists and a stale .retired dir remains, verify the .retired content is unwanted, then remove <migrations_dir>/audit-principal-home.retired manually and retry.
  3. Never reuse or restore the .retired path yourself; let the migration's resume logic validate and delete it.

Example fix

// before: ambiguous leftover blocks retry
ls ~/.astrid/migrations/  # audit-principal-home.retired exists
// after: clear the stale staging target after verifying it
rm -rf ~/.astrid/migrations/audit-principal-home.retired
migrate_legacy_audit(&home, &source)?;
Defensive patterns

Strategy: validation

Validate before calling

let retired = home.migrations_dir().join("audit-principal-home.retired");
if std::fs::symlink_metadata(&retired).is_ok() {
    // either re-run migration to resume cleanup, or remove the stale staging dir after inspection
    eprintln!("stale staging dir: {}", retired.display());
}

Type guard

fn staging_clear(home: &AstridHome) -> bool {
    std::fs::symlink_metadata(home.migrations_dir().join("audit-principal-home.retired")).is_err()
}

Try / catch

if let Err(e) = migrate_legacy_audit(&home, &source) {
    if e.kind() == std::io::ErrorKind::AlreadyExists && e.to_string().contains("ambiguous") {
        // inspect/remove audit-principal-home.retired, then retry
    }
}

Prevention

When it happens

Trigger: Calling migrate_legacy_audit twice after a first run crashed between the rename and the final delete_audit_tree, leaving the .retired staging directory behind; or any pre-existing file/directory named audit-principal-home.retired in the migrations dir.

Common situations: A previous migration was interrupted by crash/kill/power loss after rename but before deletion; leftover staging dir from a failed run that was never cleaned; manual copies placed in the migrations directory.

Understand the failure class

Background: "already exists" / EEXIST / FileAlreadyExistsException: what the 'file already exists' error means and how to fix it — this error's family across 37 libraries.

Related errors


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