astrid-runtime/astrid · error · io::Error
layout migration source contains a redirect: {}
Error message
layout migration source contains a redirect: {} What it means
During a layout-v2 migration, the inventory walk (inventory_directory, driven by inventory_tree) found a symlink inside the legacy state directory it is fingerprinting. The migration refuses to hash trees containing redirects because a symlink could change what data the inventory digest covers and could escape the state directory boundary. The operation aborts with io::ErrorKind::InvalidData naming the offending path.
Source
Thrown at crates/astrid-core/src/dirs_layout_records.rs:374
})
}
fn inventory_directory(
root: &Path,
directory: &Path,
hasher: &mut blake3::Hasher,
entries: &mut u64,
bytes: &mut u64,
) -> io::Result<()> {
let mut children = std::fs::read_dir(directory)?.collect::<Result<Vec<_>, _>>()?;
children.sort_by_key(std::fs::DirEntry::file_name);
for child in children {
let child_path = child.path();
let relative = child_path.strip_prefix(root).map_err(io::Error::other)?;
let relative_bytes = relative.as_os_str().as_encoded_bytes();
let metadata = std::fs::symlink_metadata(&child_path)?;
if metadata.file_type().is_symlink() {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!(
"layout migration source contains a redirect: {}",
child_path.display()
),
));
}
super::retirement::validate_legacy_surrealkv_entry(
relative,
metadata.is_dir(),
metadata.is_file(),
)?;
*entries = entries
.checked_add(1)
.ok_or_else(|| io::Error::other("layout inventory entry count overflow"))?;
hash_inventory_field(hasher, b"path", relative_bytes);
if metadata.is_dir() {
hasher.update(b"directory");View on GitHub (pinned to affd8760f4)
Solutions
- Find the symlink named in the message and remove it, copying real data in its place (rm the link, cp -L the target contents).
- Re-point your dotfile/symlink manager at the state directory as a whole rather than entries inside it.
- If the whole state directory should live elsewhere, migrate the entire directory (no symlinks inside) and ensure the configured path itself points to the real location.
- Re-run the migration once the tree contains only real files and directories.
Example fix
// before: state dir contains a symlink ln -s /mnt/fast/surrealkv ~/.local/state/astrid/surrealkv // after: real directory, link placed one level up mv ~/real/astrid-state ~/.local/state/astrid # move real data rm ~/.local/state/astrid/surrealkv # remove the symlink first
Defensive patterns
Strategy: validation
Validate before calling
use std::path::Path;
fn assert_no_symlinks(dir: &Path) -> std::io::Result<()> {
for entry in std::fs::read_dir(dir)? {
let entry = entry?;
let meta = std::fs::symlink_metadata(entry.path())?;
if meta.file_type().is_symlink() {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
format!("symlink in state tree: {}", entry.path().display()),
));
}
if meta.is_dir() {
assert_no_symlinks(&entry.path())?;
}
}
Ok(())
} Type guard
fn is_real_directory(p: &Path) -> bool {
std::fs::symlink_metadata(p).map(|m| m.is_dir()).unwrap_or(false)
} Prevention
- Configure dotfile managers to manage the parent directory, not entries inside the state tree.
- Audit the state tree before migrating: find <state-dir> -type l.
- Keep the state directory on the filesystem where it was created; use mounts at the top level, not symlinks inside.
When it happens
Trigger: Running a layout v2 migration (inventory_tree/inventory_directory) while any child entry of the source directory — at any depth — is a symlink (symlink_metadata reports is_symlink).
Common situations: Users symlink state directories into dotfiles managers (e.g. stow, GNU stow/dotbot) or to another disk; a backup/restore tool replaced a subdirectory with a symlink; someone hand-linked a file from elsewhere into the state tree.
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
- legacy state source is redirected or not a directory: {}
- legacy capsule directory is not a regular directory: {}
- legacy capsule entry is not a regular directory: {}
- legacy REPL history is not a regular file: {}
- Astrid home without a layout sentinel is redirected or not a
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/86be9a7686a1e692.
Report an issue: GitHub.