astrid-runtime/astrid · error
capsule materialization destination already exists
Error message
capsule materialization destination already exists
What it means
materialize_capsule_package requires a fresh destination: if symlink_metadata succeeds and the path is a real (non-symlink) directory, the extract is refused to guarantee a clean, exactly-verified materialization. This makes the function idempotence-safe — reusing a destination would mix old and new files and break receipt/hash verification.
Source
Thrown at crates/astrid-capsule-install/src/storage.rs:544
}
/// Materialize a verified durable package into a fresh disposable directory.
///
/// This helper is for loaders whose current host ABI still accepts a path. It
/// never establishes authority: callers must retain the package snapshot and
/// digest, and the destination must be a new cache generation. Archive paths,
/// links, special files, duplicate entries, and symlinked parents are rejected
/// before any bytes are written. Exact metadata and authority sidecars are
/// restored from the package bytes, not trusted from the archive.
pub fn materialize_capsule_package(
package: &CapsulePackage,
destination: &Path,
) -> anyhow::Result<()> {
match fs::symlink_metadata(destination) {
Ok(metadata) if metadata.file_type().is_symlink() || !metadata.is_dir() => {
bail!("capsule materialization destination is not a directory")
},
Ok(_) => bail!("capsule materialization destination already exists"),
Err(error) if error.kind() == io::ErrorKind::NotFound => {},
Err(error) => {
return Err(error).with_context(|| format!("inspect {}", destination.display()));
},
}
if let Some(parent) = destination.parent() {
fs::create_dir_all(parent)
.with_context(|| format!("create materialization parent {}", parent.display()))?;
}
fs::create_dir(destination)
.with_context(|| format!("create materialization {}", destination.display()))?;
let decoder = flate2::read::GzDecoder::new(Cursor::new(&package.archive));
let mut archive = tar::Archive::new(decoder);
let mut names = std::collections::BTreeSet::new();
for entry in archive.entries().context("read durable capsule archive")? {
let mut entry = entry.context("read durable capsule archive entry")?;
let path = entry.path().context("read durable capsule archive path")?;
if path.is_absolute()View on GitHub (pinned to affd8760f4)
Solutions
- Remove the existing destination directory before calling (fs::remove_dir_all).
- Use a unique destination per run (tempdir or a versioned/timestamped path).
- If the existing directory should be kept, materialize into a new sibling directory instead.
Example fix
// before
let dest = Path::new("/tmp/capsule-materialize");
materialize_capsule_package(&package, dest)?;
// after
let dest = Path::new("/tmp/capsule-materialize");
if dest.exists() {
fs::remove_dir_all(dest)?;
}
materialize_capsule_package(&package, dest)?; Defensive patterns
Strategy: validation
Validate before calling
let dest = Path::new("/tmp/capsule-materialize");
if dest.symlink_metadata().is_ok() {
fs::remove_dir_all(dest)?;
}
materialize_capsule_package(&package, dest)?; Try / catch
match materialize_capsule_package(&pkg, &dest) {
Ok(()) => {}
Err(e) if e.to_string().contains("already exists") => {
fs::remove_dir_all(&dest)?;
materialize_capsule_package(&pkg, &dest)?;
}
Err(e) => return Err(e),
} Prevention
- Use tempfile::tempdir() or a versioned path so every run gets a fresh destination.
- Clean staging directories in scripts before rerunning migrations.
- Treat materialization as non-idempotent; never call twice with the same destination.
When it happens
Trigger: Calling materialize_capsule_package twice with the same destination; running migration or tests over a destination directory left behind by a previous failed/partial run; a destination directory that already contains files.
Common situations: Re-running a migration after a crash; integration tests reusing a fixed temp dir; a deploy script that doesn't clean its staging directory between runs.
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
- capsule materialization destination is not a directory
- Capsule '{name}' is not installed.
- local capsule source {source:?} is not a regular file
- capsule projection contains a symbolic link: {}
- capsule projection contains a special file: {}
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/2f4c0baa7f48ae43.
Report an issue: GitHub.