astrid-runtime/astrid · error

durable capsule archive contains duplicate directory path

Error message

durable capsule archive contains duplicate directory path

What it means

A second directory entry resolved to a directory path already recorded in the directory set. read_archive_files keeps directories in a BTreeSet solely to detect this; a repeated directory path means the archive was assembled inconsistently, so it is rejected.

Source

Thrown at crates/astrid-capsule-install/src/storage.rs:389

        {
            bail!("durable capsule archive contains unsafe path");
        }

        let entry_type = entry.header().entry_type();
        if !entry_type.is_dir() && !entry_type.is_file() {
            bail!("durable capsule archive contains a link or special file");
        }

        let name = path
            .to_str()
            .ok_or_else(|| anyhow::anyhow!("durable capsule archive path is not UTF-8"))?
            .replace('\\', "/");
        if files.contains_key(&name) || directories.contains(&name) {
            bail!("durable capsule archive contains duplicate path {name}");
        }
        if entry_type.is_dir() {
            if !directories.insert(name) {
                bail!("durable capsule archive contains duplicate directory path");
            }
            continue;
        }

        let mut bytes = Vec::new();
        entry
            .read_to_end(&mut bytes)
            .with_context(|| format!("read durable capsule archive file {name}"))?;
        files.insert(name, bytes);
    }
    Ok(ArchiveInventory { files, directories })
}

/// Publish one source directory into the target principal's durable registry.
///
/// The source tree is canonicalized to a deterministic gzip/tar archive. The
/// package is committed as one content batch; a failed publication therefore
/// leaves the prior package authoritative. Existing durable content is used as

View on GitHub (pinned to affd8760f4)

Solutions

  1. Rebuild the archive emitting each directory at most once.
  2. Deduplicate directory entries during archive creation with a seen-set.
  3. Regenerate the archive from a single canonical source directory instead of concatenating tars.
  4. Locate the duplicated directory entry with tar -tf and fix the packaging script.

Example fix

// before: duplicate dir entries from concatenation
// cat part1.tgz part2.tgz > capsule.tgz (both include "assets/")
// after: emit each dir once during build
if directories_seen.insert(dir_name.to_string()) { write_dir_entry(dir_name); }
Defensive patterns

Strategy: validation

Validate before calling

let mut dirs = std::collections::BTreeSet::new();
if entry_type.is_dir() && !dirs.insert(name.clone()) {
    return Err(format!("duplicate directory entry {name}"));
}

Type guard

fn directory_entries_unique(dirs: &[String]) -> bool {
    dirs.iter().collect::<std::collections::BTreeSet<_>>().len() == dirs.len()
}

Try / catch

match read_verified_durable_package_for_owner(&store, owner, id).await {
    Ok(pkg) => pkg,
    Err(e) if e.to_string().contains("duplicate directory path") => {
        // regenerate the archive emitting each directory once
    },
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: read_archive_files when two entries are directories with the same normalized name, so directories.insert(name) returns false after the earlier files/directories containment check passed.

Common situations: Merged/concatenated tar archives; archive builders emitting explicit directory entries multiple times; archives generated from overlapping source directories (e.g. '.' included twice via different prefixes).

Understand the failure class

Background: "already exists" / EEXIST / FileAlreadyExistsException: what the 'file already exists' error means and how to fix it — this error's family across 37 libraries.

Related errors


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