{"record":{"id":"95af2dc8870b553d","repo":"astrid-runtime/astrid","slug":"failed-to-persist-e-95af2d","errorCode":null,"errorMessage":"failed to persist {}: {e}","messagePattern":"failed to persist (.+?): (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-cli/src/commands/distro/shuttle.rs","lineNumber":153,"sourceCode":"                        );\n                    }\n                    header.set_size(metadata.len());\n                    header.set_cksum();\n                    let mut file = std::fs::File::open(src).with_context(|| {\n                        format!(\"failed to open staged capsule {}\", src.display())\n                    })?;\n                    tar.append_data(&mut header, &entry.path, &mut file)\n                        .with_context(|| format!(\"failed to append {} to shuttle\", entry.path))?;\n                },\n            }\n        }\n\n        let encoder = tar.into_inner().context(\"failed to finish tar stream\")?;\n        encoder.finish().context(\"failed to finish gzip stream\")?;\n    }\n\n    tmp.persist(out_path)\n        .map_err(|e| anyhow::anyhow!(\"failed to persist {}: {e}\", out_path.display()))?;\n    Ok(())\n}\n\n/// Unpack a `.shuttle` at `archive_path` into `dest` (a mirror dir),\n/// WITHOUT installing anything.\n///\n/// Mirrors the hardened defense in the capsule unpacker: absolute paths\n/// and `..` traversal are refused, symlinks and hard-links are refused,\n/// and each member is size-capped. A malformed or truncated archive\n/// yields a clean error.\npub(crate) fn unpack(archive_path: &Path, dest: &Path) -> anyhow::Result<()> {\n    std::fs::create_dir_all(dest)\n        .with_context(|| format!(\"failed to create mirror dir {}\", dest.display()))?;\n\n    let tar_gz = std::fs::File::open(archive_path)\n        .with_context(|| format!(\"failed to open shuttle: {}\", archive_path.display()))?;\n    let tar = flate2::read::GzDecoder::new(tar_gz);\n    let mut archive = tar::Archive::new(tar);","sourceCodeStart":135,"sourceCodeEnd":171,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-cli/src/commands/distro/shuttle.rs#L135-L171","documentation":"pack builds the .shuttle tar+gzip stream into a temp file and then atomically persists it to the requested output path via tmp.persist(out_path). If the persist (rename) fails, the OS error is wrapped as \"failed to persist <path>: {e}\". The archive itself was built successfully; only the final move onto the destination path failed.","triggerScenarios":"Calling pack with an out_path that cannot be written: parent directory missing, no write permission on the directory, out_path is an existing directory, cross-filesystem persist where rename isn't possible, or the temp file was lost before persist.","commonSituations":"Typing an output path in a nonexistent directory (e.g. out/shuttle/pkg.shuttle when out/ doesn't exist); running from a read-only or user-mismatched directory in CI; disk full or SELinux/AppArmor blocking the rename.","solutions":["Create the output directory first (mkdir -p on the parent of out_path) and re-run pack.","Check write permissions on the target directory (and that out_path is not itself a directory).","Ensure out_path is on the same filesystem as the temp dir, or free disk space if the disk is full.","Check for mandatory-access-control denials (audit logs) if permissions look correct but the rename still fails."],"exampleFix":"// before\npack(&input, \"out/distro.shuttle\")?;   // fails if out/ doesn't exist\n// after\nstd::fs::create_dir_all(\"out\")?;\npack(&input, \"out/distro.shuttle\")?;","handlingStrategy":"validation","validationCode":"let out_path: &Path = out_path.as_ref();\nif let Some(parent) = out_path.parent() { std::fs::create_dir_all(parent)?; }\nanyhow::ensure!(!out_path.is_dir(), \"output path {} is a directory\", out_path.display());","typeGuard":null,"tryCatchPattern":"match pack(&input, out_path) {\n    Err(e) if e.to_string().starts_with(\"failed to persist\") => {\n        eprintln!(\"cannot write {}: check the directory exists and is writable ({e})\", out_path.display());\n    }\n    other => other?,\n}","preventionTips":["Always create_dir_all the parent of the output path before packing.","Run pack as a user with write access to the target directory.","Keep output paths on the same filesystem as the temp dir; monitor disk space."],"tags":["filesystem","file-write","atomic-persist","tar"],"backgroundTag":"file-write-failed","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"}