astrid-runtime/astrid · error
capsule projection contains a symbolic link: {}
Error message
capsule projection contains a symbolic link: {} What it means
This error means the walk of the capsule's projected directory found a symbolic link, which the kernel forbids: symlinks can redirect reads outside the capsule projection and bypass its containment guarantees. The library throws it during projection inventory so that any capsule whose on-disk projection contains a symlink is rejected rather than traversed.
Source
Thrown at crates/astrid-kernel/src/lib.rs:1640
) -> 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() {
inventory.files.insert(relative_text.to_owned());
} else {
anyhow::bail!(
"capsule projection contains a special file: {}",
path.display()
);
}
}
Ok(())
}View on GitHub (pinned to affd8760f4)
Solutions
- Repackage the capsule so it contains only regular files and directories (dereference symlinks at packaging time, e.g. tar -h / cp -L)
- Find the offending link (the path in the error) and replace it with a real copy of its target, or remove it
- Fix the build pipeline to not emit symlinks into the capsule output (e.g. configure the bundler to inline/duplicate linked files)
- If the symlink appeared post-materialization, investigate what wrote into the capsule directory and re-materialize
Example fix
# before: packaging preserves symlinks tar -cf capsule.capsule -C build . # after: dereference symlinks into regular files tar -h -cf capsule.capsule -C build .
Defensive patterns
Strategy: validation
Validate before calling
fn projection_has_symlinks(root: &Path) -> anyhow::Result<bool> {
for entry in walkdir::WalkDir::new(root).follow_links(false) {
if entry?.path_is_symlink() { return Ok(true); }
}
Ok(false)
} Try / catch
match result {
Err(e) if e.to_string().contains("symbolic link") => {
// clean the offending path and repackage
}
other => other?,
} Prevention
- Package capsules with symlink dereferencing (tar -h, cp -L)
- Reject symlinks in build output as a CI check
- Configure bundlers to inline or duplicate linked files
- Inspect extracted archives for links before materializing
When it happens
Trigger: Inventorying a projection (the recursive walk over the materialized capsule directory) when std::fs::symlink_metadata reports a symlink at any path within the projection — e.g. the capsule archive contained a symlink, or something on the host placed one inside the materialized directory.
Common situations: Packaging a capsule on macOS/Linux where a build step created symlinks (e.g. node_modules, vendored deps) and tar preserved them; extracting an archive with symlink-preserving flags; an attacker or misconfigured script planting symlinks in the capsule output directory.
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
- projected path is redirected or not a regular file: {}
- workspace capsule tree contains an unsafe redirect: {error}
- leftover capsule authority receipt is not a regular file: {}
- legacy capsule authority root is not a regular directory: {}
- legacy capsule authority root contains a non-regular entry:
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/a39efc444a2d64e9.
Report an issue: GitHub.