astrid-runtime/astrid · error

projected path is redirected or not a regular file: {}

Error message

projected path is redirected or not a regular file: {}

What it means

This error means a projected path failed the read-time safety check: symlink_metadata showed either a symlink (a redirect) or a non-regular file where a regular file was required. The kernel throws it in the per-file read path so it never opens anything that could redirect (TOCTOU via symlink) or block (e.g. a FIFO), and is the single-file counterpart to the projection-wide symlink/special-file checks.

Source

Thrown at crates/astrid-kernel/src/lib.rs:1673

                }
            }
            Ok(())
        }

        let mut inventory = ProjectionInventory::default();
        walk(root, root, &mut inventory)?;
        Ok(inventory)
    }

    #[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
    fn read_projection_file_nofollow(path: &Path) -> anyhow::Result<Vec<u8>> {
        use std::io::Read as _;

        let metadata = std::fs::symlink_metadata(path).map_err(|error| {
            anyhow::anyhow!("inspect projected file {}: {error}", path.display())
        })?;
        if metadata.file_type().is_symlink() || !metadata.is_file() {
            anyhow::bail!(
                "projected path is redirected or not a regular file: {}",
                path.display()
            );
        }
        let mut file = open_projection_file_nofollow(path)?;
        let mut bytes = Vec::new();
        file.read_to_end(&mut bytes)
            .map_err(|error| anyhow::anyhow!("read projected file {}: {error}", path.display()))?;
        if file.metadata()?.len() != metadata.len() || bytes.len() as u64 != metadata.len() {
            anyhow::bail!("projected file changed while read: {}", path.display());
        }
        Ok(bytes)
    }

    /// Load a capsule into the Kernel from a directory containing a Capsule.toml
    ///
    /// # Errors
    ///

View on GitHub (pinned to affd8760f4)

Solutions

  1. Re-materialize the capsule so the projection is rebuilt from the trusted archive with only regular files
  2. Verify the path passed to the read API is a regular file inside the projection (check symlink_metadata before calling)
  3. Find and remove whatever replaced the file with a symlink/special file; investigate concurrent writers to the capsule directory
  4. If packaging produced links/special files, repackage (dereference links, exclude non-regular entries)

Example fix

// before: reading without checking the entry type
let bytes = read_projected_file(&path)?;

// after: pre-check before reading
let meta = std::fs::symlink_metadata(&path)?;
if meta.file_type().is_symlink() || !meta.is_file() {
    anyhow::bail!("refusing non-regular projected path: {}", path.display());
}
let bytes = read_projected_file(&path)?;
Defensive patterns

Strategy: validation

Validate before calling

let meta = std::fs::symlink_metadata(path)?;
if meta.file_type().is_symlink() || !meta.is_file() {
    return Err("projected path must be a regular, non-symlink file");
}

Try / catch

match result {
    Err(e) if e.to_string().contains("redirected or not a regular file") => {
        // re-materialize the projection; treat as a security signal
    }
    other => other?,
}

Prevention

When it happens

Trigger: Reading a projected file (the read_projected_file path used during inventory verification) when metadata.file_type().is_symlink() is true or metadata.is_file() is false — e.g. the entry was replaced by a symlink between inventory and read, or the caller passed a path to a directory/socket/fifo.

Common situations: Concurrent modification of the capsule directory (another process swapping in symlinks); passing the wrong path (a directory) to a file-read API; a projection where a packaging bug left links or special files; an attacker redirecting a projected path via a race.

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


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