astrid-runtime/astrid · error · io::Error
layout migration source is redirected or not a directory: {}
Error message
layout migration source is redirected or not a directory: {} What it means
inventory_tree inventories a migration source directory to fingerprint it into a LayoutTreeIdentityV1. If symlink_metadata on the source succeeds but the entry is a symlink or not a directory, the function refuses with this InvalidData error rather than hashing a redirected or wrong-kind target. (A NotFound source is tolerated and hashed as "absent"; other IO errors propagate as-is.)
Source
Thrown at crates/astrid-core/src/dirs_layout_records.rs:267
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"layout destination path cannot be represented losslessly on this platform",
));
}
Ok(path)
}
pub(super) fn inventory_tree(path: &Path) -> io::Result<LayoutTreeIdentityV1> {
let mut hasher = blake3::Hasher::new_derive_key("astrid layout source inventory v1");
let mut entries = 0_u64;
let mut bytes = 0_u64;
match std::fs::symlink_metadata(path) {
Err(error) if error.kind() == io::ErrorKind::NotFound => {
hasher.update(b"absent");
},
Err(error) => return Err(error),
Ok(metadata) if metadata.file_type().is_symlink() || !metadata.is_dir() => {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!(
"layout migration source is redirected or not a directory: {}",
path.display()
),
));
},
Ok(_) => {
crate::platform_fs::verify_no_redirects(path)?;
inventory_directory(path, path, &mut hasher, &mut entries, &mut bytes)?;
},
}
Ok(LayoutTreeIdentityV1 {
path_encoding: "os-str-encoded-bytes-v1".to_owned(),
physical_path_hex: physical_path_hex(path)?,
inventory_algorithm: "blake3-derive-key-v1".to_owned(),
inventory_digest: hasher.finalize().to_hex().to_string(),
entries,View on GitHub (pinned to affd8760f4)
Solutions
- Replace the symlink at the source path with the real directory (or repoint the migration at the real directory).
- Verify the source path with `ls -la` and ensure it is an actual directory, not a file.
- If the legacy source is legitimately gone, ensure it is absent (NotFound is accepted) rather than replaced by a wrong-kind entry.
Example fix
// before: source replaced by symlink ln -s /data/new-layout /data/legacy-layout // after: real directory rm /data/legacy-layout mkdir /data/legacy-layout # or move the real directory back
Defensive patterns
Strategy: validation
Validate before calling
let md = std::fs::symlink_metadata(&source_dir)?;
match md {
md if md.is_dir() => {}, // ok
_ if md.file_type().is_symlink() => return Err("legacy source is a symlink"),
_ => return Err("legacy source is not a directory"),
} Try / catch
match retire_verified_legacy_source(...) {
Err(e) if e.to_string().contains("source is redirected or not a directory") => restore_source_directory(),
other => other,
} Prevention
- Keep legacy source directories as real directories; never symlink them.
- Audit for symlinks with `find <root> -type l` before migrating.
- If a legacy source is gone, delete it entirely (absent is accepted) rather than substituting a file.
When it happens
Trigger: Called from LayoutRetirementV1, retire_verified_legacy_source, or LayoutMigrationMaterialV1 when the source path is a symlink to a directory, a regular file, or another non-directory entry.
Common situations: Legacy source directory replaced by a symlink to consolidated storage; migration re-run after the source directory was deleted and a file created in its place; users pointing migration at a file instead of the directory.
Related errors
- layout migration destination is redirected or not a regular
- legacy principal home root is not a regular directory: {}
- legacy principal profile is not a regular file: {}
- legacy capsule directory is not a regular directory: {}
- legacy capsule entry is not a regular directory: {}
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/d3048f2f55d9007c.
Report an issue: GitHub.