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

  1. 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)
  2. Remove or rename any non-directory file occupying a path that must be a directory (e.g. a file named var)
  3. Keep the entire Astrid home on real directories; use Astrid's own volume/storage mechanisms rather than symlinks for relocation
  4. 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

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


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