{"record":{"id":"2f4c0baa7f48ae43","repo":"astrid-runtime/astrid","slug":"capsule-materialization-destination-already-exists","errorCode":null,"errorMessage":"capsule materialization destination already exists","messagePattern":"capsule materialization destination already exists","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-capsule-install/src/storage.rs","lineNumber":544,"sourceCode":"}\n\n/// Materialize a verified durable package into a fresh disposable directory.\n///\n/// This helper is for loaders whose current host ABI still accepts a path. It\n/// never establishes authority: callers must retain the package snapshot and\n/// digest, and the destination must be a new cache generation. Archive paths,\n/// links, special files, duplicate entries, and symlinked parents are rejected\n/// before any bytes are written. Exact metadata and authority sidecars are\n/// restored from the package bytes, not trusted from the archive.\npub fn materialize_capsule_package(\n    package: &CapsulePackage,\n    destination: &Path,\n) -> anyhow::Result<()> {\n    match fs::symlink_metadata(destination) {\n        Ok(metadata) if metadata.file_type().is_symlink() || !metadata.is_dir() => {\n            bail!(\"capsule materialization destination is not a directory\")\n        },\n        Ok(_) => bail!(\"capsule materialization destination already exists\"),\n        Err(error) if error.kind() == io::ErrorKind::NotFound => {},\n        Err(error) => {\n            return Err(error).with_context(|| format!(\"inspect {}\", destination.display()));\n        },\n    }\n    if let Some(parent) = destination.parent() {\n        fs::create_dir_all(parent)\n            .with_context(|| format!(\"create materialization parent {}\", parent.display()))?;\n    }\n    fs::create_dir(destination)\n        .with_context(|| format!(\"create materialization {}\", destination.display()))?;\n    let decoder = flate2::read::GzDecoder::new(Cursor::new(&package.archive));\n    let mut archive = tar::Archive::new(decoder);\n    let mut names = std::collections::BTreeSet::new();\n    for entry in archive.entries().context(\"read durable capsule archive\")? {\n        let mut entry = entry.context(\"read durable capsule archive entry\")?;\n        let path = entry.path().context(\"read durable capsule archive path\")?;\n        if path.is_absolute()","sourceCodeStart":526,"sourceCodeEnd":562,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-capsule-install/src/storage.rs#L526-L562","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nlet dest = Path::new(\"/tmp/capsule-materialize\");\nmaterialize_capsule_package(&package, dest)?;\n// after\nlet dest = Path::new(\"/tmp/capsule-materialize\");\nif dest.exists() {\n    fs::remove_dir_all(dest)?;\n}\nmaterialize_capsule_package(&package, dest)?;","handlingStrategy":"validation","validationCode":"let dest = Path::new(\"/tmp/capsule-materialize\");\nif dest.symlink_metadata().is_ok() {\n    fs::remove_dir_all(dest)?;\n}\nmaterialize_capsule_package(&package, dest)?;","typeGuard":null,"tryCatchPattern":"match materialize_capsule_package(&pkg, &dest) {\n    Ok(()) => {}\n    Err(e) if e.to_string().contains(\"already exists\") => {\n        fs::remove_dir_all(&dest)?;\n        materialize_capsule_package(&pkg, &dest)?;\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["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."],"tags":["filesystem","destination-exists","capsule"],"backgroundTag":"file-already-exists","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}