astrid-runtime/astrid · error
legacy secrets root is not a regular directory
Error message
legacy secrets root is not a regular directory: {} What it means
`ensure_legacy_secret_aliases` validates the legacy secrets root before creating aliases. If the root exists but is a symlink or not a directory, the function fails with `InvalidData` rather than aliasing into an unverified location.
Solutions
- Replace the symlink/file with a real directory containing the legacy secret files.
- Recreate the legacy secrets root at its expected path and re-run migration.
- Exclude the secrets root from symlink-managing tools.
Example fix
# before secrets -> ~/dotfiles/secrets # after rm secrets && mkdir secrets && cp -L ~/dotfiles/secrets/* secrets/
Defensive patterns
Strategy: validation
Validate before calling
fn secrets_root_ok(root: &std::path::Path) -> bool {
match std::fs::symlink_metadata(root) {
Ok(m) => m.is_dir() && !m.file_type().is_symlink(),
Err(e) => e.kind() == std::io::ErrorKind::NotFound,
}
} Type guard
fn is_real_dir_or_missing(p: &std::path::Path) -> bool {
!std::path::Path::new(p).symlink_metadata().map(|m| m.file_type().is_symlink() || !m.is_dir()).unwrap_or(false)
} Try / catch
match ensure_legacy_secret_aliases(root, ...) {
Err(e) if e.kind() == std::io::ErrorKind::InvalidData
&& e.to_string().contains("not a regular directory") => {
eprintln!("recreate the legacy secrets root as a real directory");
return Err(e.into());
}
other => other,
} Prevention
- Verify the secrets root with `ls -la` (no `l` prefix) before migration.
- Recreate symlinked roots as real directories with copied content.
- Document that the legacy secrets root must never be symlinked.
When it happens
Trigger: Calling `ensure_legacy_secret_aliases` when `symlink_metadata(root)` succeeds but `is_symlink()` is true or `is_dir()` is false.
Common situations: The whole legacy secrets directory was symlinked (dotfiles manager, backup restore creating symlinks); a file overwrote the secrets root path.
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/75525848ac941166.
Report an issue: GitHub.
Appendix: source
Thrown at crates/astrid-kernel/src/legacy_migration_barrier/host_fs.rs:89
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,
allow_empty_cleanup: bool,
) -> io::Result<()> {
let metadata = match fs::symlink_metadata(root) {
Ok(metadata) => metadata,
Err(error) if error.kind() == io::ErrorKind::NotFound => return Ok(()),
Err(error) => return Err(error),
};
if metadata.file_type().is_symlink() || !metadata.is_dir() {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!(
"legacy secrets root is not a regular directory: {}",
root.display()
),
));
}
astrid_core::platform_fs::validate_private_directory(root)?;
astrid_core::platform_fs::verify_no_redirects(root)?;
let entries = fs::read_dir(root)
.map_err(io::Error::other)?
.collect::<Result<Vec<_>, _>>()
.map_err(io::Error::other)?;
for entry in entries {
if entry.file_name() == "__host__" {
continue;
}
let path = entry.path();View on GitHub (pinned to affd8760f4)