{"record":{"id":"f5e2bc216780e30f","repo":"astrid-runtime/astrid","slug":"runtime-projection-is-redirected","errorCode":null,"errorMessage":"runtime projection is redirected: {}","messagePattern":"runtime projection is redirected: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-core/src/dirs_projection_retirement.rs","lineNumber":44,"sourceCode":"        let path = entry.path();\n        if entry.file_name() == std::ffi::OsStr::new(CANONICAL_VOLUME) {\n            continue;\n        }\n        let metadata = std::fs::symlink_metadata(&path)?;\n        if metadata.is_dir() {\n            crate::dirs::retire_legacy_source_tree(&path)?;\n        } else {\n            std::fs::remove_file(&path)?;\n        }\n    }\n    File::open(root)?.sync_all()?;\n    Ok(())\n}\n\nfn preflight_projection_entry(path: &Path) -> io::Result<()> {\n    let metadata = std::fs::symlink_metadata(path)?;\n    if metadata.file_type().is_symlink() {\n        return Err(io::Error::new(\n            io::ErrorKind::InvalidData,\n            format!(\"runtime projection is redirected: {}\", path.display()),\n        ));\n    }\n    if metadata.is_dir() {\n        for entry in std::fs::read_dir(path)? {\n            let child = entry?.path();\n            preflight_projection_entry(&child)?;\n        }\n        return Ok(());\n    }\n    if !metadata.is_file() {\n        return Err(io::Error::new(\n            io::ErrorKind::InvalidData,\n            format!(\n                \"runtime projection contains a special entry: {}\",\n                path.display()\n            ),","sourceCodeStart":26,"sourceCodeEnd":62,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-core/src/dirs_projection_retirement.rs#L26-L62","documentation":"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.","triggerScenarios":"Any file or directory inside the runtime projection root is a symlink when retire_projection_root (or the recursive preflight) runs.","commonSituations":"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.","solutions":["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.","Scan first: find <projection-root> -type l to enumerate all redirects before invoking retirement.","Change deployment processes so projection outputs are always materialized as real files."],"exampleFix":"// before\nprojection/assets -> /opt/shared/assets\n// after\nrm projection/assets\ncp -aL /opt/shared/assets projection/assets","handlingStrategy":"validation","validationCode":"fn assert_no_links(root: &Path) -> io::Result<()> {\n    for e in walkdir_like(root) {\n        if std::fs::symlink_metadata(&e)?.file_type().is_symlink() {\n            return Err(io::Error::new(io::ErrorKind::InvalidData, format!(\"link: {}\", e.display())));\n        }\n    }\n    Ok(())\n}","typeGuard":"fn is_redirect(p: &Path) -> bool {\n    std::fs::symlink_metadata(p).map(|m| m.file_type().is_symlink()).unwrap_or(false)\n}","tryCatchPattern":"if let Err(e) = retire_projection_root(&path) {\n    if e.kind() == io::ErrorKind::InvalidData { eprintln!(\"materialize or remove link: {e}\"); }\n}","preventionTips":["Generate projection files in place rather than linking to shared assets","Scan with `find <projection-root> -type l` before cleanup","Make deployment scripts dereference (-h/-L) when copying into the projection tree"],"tags":["rust","symlink","filesystem"],"backgroundTag":"symlink-detected-in-path","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}