astrid-runtime/astrid · error
legacy source is not an empty regular directory
Error message
legacy source is not an empty regular directory: {} What it means
`retire_empty_directory` removes an empty legacy source directory only if it is a real, non-symlink directory. Anything else (symlink, file) is rejected with `InvalidData` to guarantee retirement never deletes through a redirect.
Solutions
- Remove the symlink or file at the path and create/verify a real empty directory before migration.
- Point the migration at the genuine legacy directory path.
- Clean up the legacy layout so only real directories remain.
Example fix
# before legacy-secrets -> /mnt/backup/legacy-secrets # after rm legacy-secrets && mkdir legacy-secrets && copy files in
Defensive patterns
Strategy: validation
Validate before calling
fn retire_ok(p: &std::path::Path) -> bool {
std::fs::symlink_metadata(p)
.map(|m| !m.file_type().is_symlink() && m.is_dir())
.unwrap_or(false)
} Type guard
fn is_plain_directory(p: &std::path::Path) -> bool {
std::fs::symlink_metadata(p).map(|m| m.is_dir() && !m.file_type().is_symlink()).unwrap_or(false)
} Try / catch
if is_plain_directory(legacy_dir) {
retire_empty_directory(legacy_dir)?;
} else {
eprintln!("legacy dir is a symlink/file; normalize before migration");
} Prevention
- Normalize legacy directories (no symlinks) before running migration.
- Check with `find legacy -type l` before migration.
- Keep legacy roots out of symlink-managed dotfiles setups.
When it happens
Trigger: Called from `ensure_legacy_secret_aliases`, `import_legacy_system_secrets`, or `handle_non_default_audit_source` when the path's `symlink_metadata` reports `is_symlink()` or not `is_dir()`.
Common situations: Legacy secret dir was symlinked to a dotfiles repo; the 'directory' was replaced by a file; a sync client substituted 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
- InvalidData
- layout migration destination is redirected or not a regular…
- layout migration source contains a redirect
- layout migration source is redirected or not a directory
- legacy audit source is not a regular directory
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/816f57e8cc11ec0e.
Report an issue: GitHub.
Appendix: source
Thrown at crates/astrid-kernel/src/legacy_migration_barrier/host_fs.rs:62
sources,
format!("principal:{uid}:env:{capsule}"),
home.principal_home(alias)
.env_dir()
.join(format!("{capsule}.env.json")),
)?;
add_source(
sources,
format!("principal:{uid}:secret:{capsule}"),
home.secrets_dir().join(alias.as_str()).join(capsule),
)?;
}
Ok(())
}
pub(super) fn retire_empty_directory(path: &Path) -> io::Result<()> {
let metadata = fs::symlink_metadata(path)?;
if metadata.file_type().is_symlink() || !metadata.is_dir() {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!(
"legacy source is not an empty regular directory: {}",
path.display()
),
));
}
astrid_core::platform_fs::verify_no_redirects(path)?;
fs::remove_dir(path).map_err(io::Error::other)?;
sync_parent(path)
}
/// Check every alias-keyed child of the released `secrets/` root. The
/// barrier passes `allow_empty_cleanup=false` while resuming a completed
/// ledger, so a deleted or renamed principal cannot leave a reappeared empty
/// directory that is silently swept on restart.
pub(super) fn ensure_legacy_secret_aliases(
root: &Path,View on GitHub (pinned to affd8760f4)