astrid-runtime/astrid · error · io::Error
layout path is redirected or not a directory: {}
Error message
layout path is redirected or not a directory: {} What it means
verify_existing_ancestor, part of the preflight_layout_v2_paths check run by the migration APIs, walks each home path (root, var/, migrations/, principal-store, content staging, cow/, state.db) looking for the nearest existing ancestor. If that ancestor is a symlink or not a directory, migration is refused with InvalidData. This is a no-follow guard against redirected home layouts, where part of the path could silently point at another location or volume.
Source
Thrown at crates/astrid-core/src/dirs_layout.rs:426
&self.storage_volume_path(),
)?;
retire_legacy_source_tree(&self.state_db_path())
}
},
}
}
fn ensure_private_dir(path: &Path) -> io::Result<()> {
crate::platform_fs::ensure_private_directory(path)
}
}
fn verify_existing_ancestor(path: &Path) -> io::Result<()> {
let mut candidate = path;
loop {
match std::fs::symlink_metadata(candidate) {
Ok(metadata) if metadata.file_type().is_symlink() || !metadata.is_dir() => {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!(
"layout path is redirected or not a directory: {}",
candidate.display()
),
));
},
Ok(_) => return crate::platform_fs::verify_no_redirects(candidate),
Err(error) if error.kind() == io::ErrorKind::NotFound => {
candidate = candidate.parent().ok_or_else(|| {
io::Error::new(
io::ErrorKind::InvalidData,
format!(
"layout path has no existing directory ancestor: {}",
path.display()
),
)
})?;View on GitHub (pinned to affd8760f4)
Solutions
- Run ls -la on each path component of the reported path and replace any symlink with a real directory (move the data, remove the link, copy/move contents back)
- Remove or rename any non-directory file occupying a path that must be a directory (e.g. a file named var)
- Keep the entire Astrid home on real directories; use Astrid's own volume/storage mechanisms rather than symlinks for relocation
- If the redirect is intentional (e.g. home on another volume), relocate the whole home root instead of individual subdirectories
Example fix
// before: var is a symlink to /mnt/big/astrid-var ln -s /mnt/big/astrid-var ~/.astrid/var home.begin_layout_v2_migration(&target)?; // InvalidData: redirected path // after mv ~/.astrid/var /mnt/big/astrid-var.bak && rm ~/.astrid/var mv /mnt/big/astrid-var.bak ~/.astrid/var # real directory, no symlink
Defensive patterns
Strategy: validation
Validate before calling
for p in [root.join("var"), root.join("var/migrations"), root.join("etc")] {
let md = std::fs::symlink_metadata(&p)?;
if md.file_type().is_symlink() || !md.is_dir() {
return Err(anyhow!("{} is a symlink or not a directory", p.display()));
}
} Type guard
fn is_real_directory(p: &Path) -> bool {
std::fs::symlink_metadata(p).map(|m| m.is_dir() && !m.file_type().is_symlink()).unwrap_or(false)
} Try / catch
match home.begin_layout_v2_migration(&target) {
Err(e) if e.to_string().contains("redirected or not a directory") => {
// replace the symlink with a real directory before retrying
},
r => r?,
} Prevention
- Never symlink subdirectories of the Astrid home; move the entire home instead
- Audit for symlinks after restores or disk migrations: find <home> -type l
- Let the library create its own directory skeleton via ensure
- Use volume/storage features provided by the library for relocation needs
When it happens
Trigger: Calling begin_layout_v2_migration or complete_layout_v2 when any component of the Astrid home paths (e.g. var, var/migrations, state.db's parent chain) resolves through a symlink or an existing non-directory entry (a regular file where a directory is expected).
Common situations: Symlinking ~/.astrid/var to another disk; a stray regular file named 'var' inside the home; overlay/restore tooling substituting directories with links; moving part of the home with ln -s to save space.
Related errors
- capsule materialization target is redirected or not a direct
- layout migration receipt is redirected or not a regular file
- legacy state source is redirected: {}
- legacy state source contains a redirect: {}
- private path is redirected: {}
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/18ec6b0162fbc698.
Report an issue: GitHub.