astrid-runtime/astrid · error

legacy env/secret path is not a regular directory: {}

Error message

legacy env/secret path is not a regular directory: {}

What it means

While checking whether legacy env/secret entries exist, the code stats the path with symlink_metadata and requires it to be a real (non-symlink) directory. If the path is a symlink or any non-directory file type, it bails with this error rather than silently treating it as present or absent. This protects against hostile or accidental symlinks pointing the legacy importer at unexpected locations.

Source

Thrown at crates/astrid-capsule-install/src/storage/migration.rs:347

        statuses.push(LegacyEnvSecretImportStatus {
            uid,
            alias,
            native_env_present,
            native_secret_present,
            unreceipted_capsules,
        });
    }
    Ok(statuses)
}

fn legacy_entries_present(path: &Path) -> anyhow::Result<bool> {
    let metadata = match fs::symlink_metadata(path) {
        Ok(metadata) => metadata,
        Err(error) if error.kind() == io::ErrorKind::NotFound => return Ok(false),
        Err(error) => return Err(error).with_context(|| format!("inspect {}", path.display())),
    };
    if metadata.file_type().is_symlink() || !metadata.is_dir() {
        bail!(
            "legacy env/secret path is not a regular directory: {}",
            path.display()
        );
    }
    let mut entries = fs::read_dir(path).with_context(|| format!("read {}", path.display()))?;
    Ok(entries.next().transpose()?.is_some())
}

#[cfg(test)]
mod tests {
    use std::sync::Arc;

    use astrid_core::identity::PrincipalUid;
    use astrid_storage::{
        KvQuotaResolver, PrincipalDirectory, StateOwner,
        open_runtime_principal_store_with_directory,
    };

View on GitHub (pinned to affd8760f4)

Solutions

  1. Replace the symlink with a real directory (move the symlink's target contents into place, or remove the symlink and re-create the directory).
  2. If it is a regular file, remove or rename it and recreate the expected directory.
  3. Re-run the import after the path is a plain directory.

Example fix

// before: legacy path is a symlink → bail
~/.astrid/legacy/env -> /mnt/dotfiles/env

// after: make it a real directory
rm ~/.astrid/legacy/env
mkdir -p ~/.astrid/legacy/env
cp -a /mnt/dotfiles/env/. ~/.astrid/legacy/env/
Defensive patterns

Strategy: validation

Validate before calling

let meta = std::fs::symlink_metadata(path)?;
if meta.file_type().is_symlink() || !meta.is_dir() {
    eprintln!("{} must be a real directory; fix before import", path.display());
}

Type guard

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

Try / catch

match import_env_and_secrets(&home, &directory) {
    Ok(s) => { /* ... */ }
    Err(e) if e.to_string().contains("not a regular directory") => {
        eprintln!("replace the symlink/file at the legacy path with a real directory");
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: legacy_env_secret_import_status → legacy_entries_present is called and the legacy env/secret path is a symlink, a regular file, a FIFO, etc., instead of an actual directory.

Common situations: Users replaced the legacy directory with a symlink into a synced/Dotfiles-managed location (e.g. ~/dotfiles/env → ~/.astrid/legacy/env); packaging left a regular file where the directory should be; restore tools replaced directories with symlinks.

Related errors


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