astrid-runtime/astrid · error
capsule projection escaped its root: {}
Error message
capsule projection escaped its root: {} What it means
Thrown when `path.strip_prefix(root)` fails while walking a capsule projection, meaning an entry's path is not under the projection root. This is a safety check against path traversal — a projection entry escaping its declared root is treated as corruption/attack and aborts the walk.
Source
Thrown at crates/astrid-kernel/src/lib.rs:1630
Ok(())
}
/// Inventory a projection without traversing redirects or special files.
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
fn inventory_projection_files(root: &Path) -> anyhow::Result<ProjectionInventory> {
fn walk(
root: &Path,
directory: &Path,
inventory: &mut ProjectionInventory,
) -> anyhow::Result<()> {
for entry in std::fs::read_dir(directory).map_err(|error| {
anyhow::anyhow!("read capsule projection {}: {error}", directory.display())
})? {
let entry = entry
.map_err(|error| anyhow::anyhow!("read capsule projection entry: {error}"))?;
let path = entry.path();
let relative = path.strip_prefix(root).map_err(|_| {
anyhow::anyhow!("capsule projection escaped its root: {}", path.display())
})?;
let relative_text = relative.to_str().ok_or_else(|| {
anyhow::anyhow!("capsule projection path is not UTF-8: {}", path.display())
})?;
let metadata = std::fs::symlink_metadata(&path).map_err(|error| {
anyhow::anyhow!("inspect capsule projection {}: {error}", path.display())
})?;
let file_type = metadata.file_type();
if file_type.is_symlink() {
anyhow::bail!(
"capsule projection contains a symbolic link: {}",
path.display()
);
}
if file_type.is_dir() {
inventory.directories.insert(relative_text.to_owned());
walk(root, &path, inventory)?;
} else if file_type.is_file() {View on GitHub (pinned to affd8760f4)
Solutions
- Do not pass mismatched root/directory arguments — directory must equal root or be beneath it.
- Rebuild/re-extract the capsule projection; treat the existing projection as corrupted.
- Verify the capsule source is trusted and free of out-of-root symlinks before projection.
- If intentional, project only content confined to the root.
Example fix
// before walk(&subdir_root, &root, &mut inv)?; // mismatched // after walk(&root, &root, &mut inv)?;
Defensive patterns
Strategy: validation
Validate before calling
// reject out-of-root links before projecting
for entry in walk_source(capsule_dir) {
let meta = std::fs::symlink_metadata(&entry)?;
if meta.file_type().is_symlink() {
anyhow::bail!("refusing to project symlink: {}", entry.display());
}
} Type guard
fn stays_under_root(root: &Path, p: &Path) -> bool { p.strip_prefix(root).is_ok() } Try / catch
if let Err(e) = load_projection(root) {
if e.to_string().contains("escaped its root") {
// treat projection as corrupted: rebuild from a trusted capsule source
}
} Prevention
- Only project capsules from trusted, validated sources.
- Reject symlinks at capsule build/pack time.
- Never pass a root different from the directory being walked.
When it happens
Trigger: A capsule projection directory contains a symlink or hardlink whose resolved path lies outside the root, or the walk is invoked with mismatched root/directory arguments.
Common situations: Malicious or corrupted capsule content planting symlinks pointing outside the projection; calling walk with a subdirectory as `root` but a sibling path; inconsistent root after a rename/move.
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
- symlink {} resolves outside the capsule source root ({}); re
- directory symlink {} not allowed in capsule source tree (ref
- mountpoint contains traversal: {}
- capsule path escaped source root
- legacy state source is redirected: {}
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/e22441fae7cbe8d4.
Report an issue: GitHub.