astrid-runtime/astrid · error

runtime projection is redirected

Error message

runtime projection is redirected: {}

What it means

preflight_projection_entry validates each entry of the runtime projection tree before retirement and rejects any symlink with InvalidData. Like the legacy source checks, this prevents deleting through redirects that could point outside the projection root. It recurses into real directories and is re-entered for every child.

Solutions

  1. Replace the symlink with a real copy of its target inside the projection tree (cp -aL), or delete the link if it is disposable, then retry.
  2. Scan first: find <projection-root> -type l to enumerate all redirects before invoking retirement.
  3. Change deployment processes so projection outputs are always materialized as real files.

Example fix

// before
projection/assets -> /opt/shared/assets
// after
rm projection/assets
cp -aL /opt/shared/assets projection/assets
Defensive patterns

Strategy: validation

Validate before calling

fn assert_no_links(root: &Path) -> io::Result<()> {
    for e in walkdir_like(root) {
        if std::fs::symlink_metadata(&e)?.file_type().is_symlink() {
            return Err(io::Error::new(io::ErrorKind::InvalidData, format!("link: {}", e.display())));
        }
    }
    Ok(())
}

Type guard

fn is_redirect(p: &Path) -> bool {
    std::fs::symlink_metadata(p).map(|m| m.file_type().is_symlink()).unwrap_or(false)
}

Try / catch

if let Err(e) = retire_projection_root(&path) {
    if e.kind() == io::ErrorKind::InvalidData { eprintln!("materialize or remove link: {e}"); }
}

Prevention

When it happens

Trigger: Any file or directory inside the runtime projection root is a symlink when retire_projection_root (or the recursive preflight) runs.

Common situations: Deployment tooling symlinked runtime projection files to shared assets; user replaced generated files with links to their own copies; a partial previous cleanup left links behind.

Related errors


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

Appendix: source

Thrown at crates/astrid-core/src/dirs_projection_retirement.rs:44

        let path = entry.path();
        if entry.file_name() == std::ffi::OsStr::new(CANONICAL_VOLUME) {
            continue;
        }
        let metadata = std::fs::symlink_metadata(&path)?;
        if metadata.is_dir() {
            crate::dirs::retire_legacy_source_tree(&path)?;
        } else {
            std::fs::remove_file(&path)?;
        }
    }
    File::open(root)?.sync_all()?;
    Ok(())
}

fn preflight_projection_entry(path: &Path) -> io::Result<()> {
    let metadata = std::fs::symlink_metadata(path)?;
    if metadata.file_type().is_symlink() {
        return Err(io::Error::new(
            io::ErrorKind::InvalidData,
            format!("runtime projection is redirected: {}", path.display()),
        ));
    }
    if metadata.is_dir() {
        for entry in std::fs::read_dir(path)? {
            let child = entry?.path();
            preflight_projection_entry(&child)?;
        }
        return Ok(());
    }
    if !metadata.is_file() {
        return Err(io::Error::new(
            io::ErrorKind::InvalidData,
            format!(
                "runtime projection contains a special entry: {}",
                path.display()
            ),

View on GitHub (pinned to affd8760f4)