astrid-runtime/astrid · error

capsule symlink resolves outside its source tree

Error message

capsule symlink resolves outside its source tree: {}

What it means

Fired by collect_directory_records when a symlink inside a capsule content tree canonicalizes to a path outside the capsule's canonical root. Treated as an escape attempt to keep the verified content confined to its source tree.

Solutions

  1. Replace the symlink with a regular copy of the target file inside the tree
  2. Remove the symlink from the capsule content directory
  3. Rebuild the capsule without external links
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at crates/astrid-build/src/artifact.rs:467 when the library encounters an invalid state.

Common situations: See trigger scenarios.


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

Appendix: source

Thrown at crates/astrid-build/src/artifact.rs:467

fn collect_directory_records(
    root: &Path,
    canonical_root: &Path,
    current: &Path,
    records: &mut Vec<ContentRecord>,
    envelope: &mut Option<Vec<u8>>,
) -> anyhow::Result<()> {
    let mut entries = std::fs::read_dir(current)
        .with_context(|| format!("failed to read capsule directory {}", current.display()))?
        .collect::<Result<Vec<_>, _>>()?;
    entries.sort_unstable_by_key(std::fs::DirEntry::file_name);
    for entry in entries {
        let path = entry.path();
        let file_type = entry.file_type()?;
        if file_type.is_symlink() {
            let resolved = std::fs::canonicalize(&path)
                .with_context(|| format!("failed to resolve capsule symlink {}", path.display()))?;
            if !resolved.starts_with(canonical_root) {
                bail!(
                    "capsule symlink resolves outside its source tree: {}",
                    path.display()
                );
            }
            let metadata = std::fs::metadata(&resolved)?;
            if !metadata.is_file() {
                bail!(
                    "capsule content cannot contain a directory symlink: {}",
                    path.display()
                );
            }
            let relative = path.strip_prefix(root)?;
            let normalized = normalize_relative_path(relative)?;
            let mut file = File::open(&resolved)?;
            records.push(hash_reader(normalized, metadata.len(), &mut file)?);
            continue;
        }
        if file_type.is_dir() {

View on GitHub (pinned to affd8760f4)