astrid-runtime/astrid · error
legacy state source contains a redirect: {}
Error message
legacy state source contains a redirect: {} What it means
Thrown when an entry inside the legacy state source tree is a symlink. The retirement path is strictly no-follow: every child is checked with symlink_metadata before being validated or deleted, and any redirect is rejected so deletion can never act through a link onto an outside target.
Source
Thrown at crates/astrid-core/src/dirs_layout_retirement.rs:62
io::ErrorKind::InvalidData,
format!("legacy state source is redirected: {}", path.display()),
));
}
if !metadata.is_dir() {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("legacy state source is not a directory: {}", path.display()),
));
}
crate::platform_fs::verify_no_redirects(path)?;
ensure_legacy_tree_boundary(path, root_device, &metadata)?;
for entry in std::fs::read_dir(path)? {
let entry = entry?;
let child = entry.path();
let child_metadata = std::fs::symlink_metadata(&child)?;
if child_metadata.file_type().is_symlink() {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!(
"legacy state source contains a redirect: {}",
child.display()
),
));
}
ensure_legacy_tree_boundary(&child, root_device, &child_metadata)?;
if child_metadata.is_dir() {
validate_legacy_tree(&child, root_device)?;
} else if child_metadata.is_file() {
// Opening only after the no-follow validation ensures a replaced
// symlink is rejected rather than read or removed through it.
crate::platform_fs::verify_no_redirects(&child)?;
} else {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!(View on GitHub (pinned to affd8760f4)
Solutions
- Remove the symlinked entries and copy/move the real data into the tree so every entry is a regular file or directory (bind mounts are also rejected at boundaries, so use physical copies)
- Check whether an earlier relocation or disk-space workaround created the links; undo that layout
- Verify no external tooling (backups, cleanup cron jobs) reintroduces symlinks, then re-run retirement
Example fix
// before: linked data file inside the legacy tree /srv/astrid/var/state/sstables/00000000000000000001.sst -> /mnt/big/00000000000000000001.sst // after: place a real copy in the tree rm /srv/astrid/var/state/sstables/00000000000000000001.sst cp /mnt/big/00000000000000000001.sst /srv/astrid/var/state/sstables/
Defensive patterns
Strategy: validation
Validate before calling
fn tree_has_symlinks(root: &Path) -> std::io::Result<Vec<std::path::PathBuf>> {
let mut found = Vec::new();
for entry in std::fs::read_dir(root)? {
let child = entry?.path();
if std::fs::symlink_metadata(&child)?.file_type().is_symlink() {
found.push(child);
} else if child.is_dir() {
found.extend(tree_has_symlinks(&child)?);
}
}
Ok(found)
} Type guard
fn is_redirect(path: &Path) -> bool {
std::fs::symlink_metadata(path)
.map(|m| m.file_type().is_symlink())
.unwrap_or(false)
} Try / catch
match retire_legacy_source_tree(&path) {
Err(e) if e.to_string().contains("contains a redirect") => {
// parse offending child from the message, replace symlink with real data
}
other => other?,
} Prevention
- Keep all legacy state data physically inside the tree; never symlink large files elsewhere
- Warn users that relocating files via symlinks blocks migration/retirement
- Run a symlink scan over the state tree before upgrading or retiring
When it happens
Trigger: validate_legacy_tree enumerates the tree and finds child_metadata.file_type().is_symlink() for any child — e.g. a symlinked data file (sstables, wal, manifest), a linked subdirectory, or a convenience link created by tooling inside the legacy state directory.
Common situations: Administrators symlinking large WAL/SSTable files onto another volume to save space; moving part of the tree and linking it back; backup tools leaving symlink placeholders; restore from a layout that used links.
Understand the failure class
Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.
Related errors
- legacy state source is redirected: {}
- directory symlink {} not allowed in capsule source tree (ref
- inspect projected file {}: {error}
- legacy state source changed type: {}
- private path is redirected: {}
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/7454ce645b15a5a6.
Report an issue: GitHub.