{"record":{"id":"c16116d1546363f3","repo":"astrid-runtime/astrid","slug":"empty-archive-path","errorCode":null,"errorMessage":"empty archive path","messagePattern":"empty archive path","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-cli/src/commands/distro/shuttle.rs","lineNumber":254,"sourceCode":"            .with_context(|| format!(\"failed to unpack {}\", out_path.display()))?;\n    }\n    Ok(())\n}\n\n/// The archive-relative path of a capsule member: `capsules/<name>.capsule`.\npub(crate) fn capsule_member_path(name: &str) -> String {\n    format!(\"{CAPSULES_DIR}/{name}.capsule\")\n}\n\n/// The on-disk path of a capsule inside an unpacked mirror.\npub(crate) fn capsule_mirror_path(mirror: &Path, name: &str) -> PathBuf {\n    mirror.join(CAPSULES_DIR).join(format!(\"{name}.capsule\"))\n}\n\n/// Reject archive paths that are absolute, contain `..`, or are empty.\nfn validate_archive_path(path: &str) -> anyhow::Result<()> {\n    if path.is_empty() {\n        bail!(\"empty archive path\");\n    }\n    let p = Path::new(path);\n    if p.is_absolute() || p.components().any(|c| matches!(c, Component::ParentDir)) {\n        bail!(\"unsafe archive path '{path}'\");\n    }\n    Ok(())\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    fn sample_entries() -> Vec<ShuttleEntry> {\n        vec![\n            ShuttleEntry {\n                path: SIG_NAME.to_string(),\n                content: ShuttleContent::Bytes(b\"deadbeef\".to_vec()),\n            },","sourceCodeStart":236,"sourceCodeEnd":272,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-cli/src/commands/distro/shuttle.rs#L236-L272","documentation":"`validate_archive_path` rejects an empty string before any path checks, since an empty archive path cannot name a capsule. This is a guard so downstream pack logic always receives a usable destination path.","triggerScenarios":"Calling `pack` (which calls `validate_archive_path`) with an empty output/`--out` path argument, e.g. `astrid distro pack --out \"\"`.","commonSituations":"A shell variable holding the output path is unset/empty (e.g. `--out \"$OUT\"` with `OUT=`); a script generating the path from an empty config field.","solutions":["Pass a non-empty output path, e.g. `--out distro/my.capsule`.","Fix the script/config so the path variable is populated (quote and check it: `: \"${OUT:?OUT is empty}\"`).","Rely on the default destination (mirrors `<name>.capsule`) by omitting the path argument instead of passing an empty one."],"exampleFix":"// before\n$ astrid distro pack --out \"$CAPSULE\"   # CAPSULE empty\n// after\n$ : \"${CAPSULE:?set CAPSULE}\" && astrid distro pack --out \"$CAPSULE\"","handlingStrategy":"validation","validationCode":"if out_path.trim().is_empty() {\n    eprintln!(\"--out path is required\");\n    std::process::exit(2);\n}","typeGuard":null,"tryCatchPattern":"match pack(&staging, out) {\n    Err(e) if e.to_string().contains(\"empty archive path\") => {\n        eprintln!(\"supply a non-empty --out path, e.g. --out my.capsule\");\n    }\n    other => other?,\n}","preventionTips":["Always pass an explicit, quoted --out value","Use `${VAR:?msg}` in scripts to catch unset path variables","Omit the flag to use the default destination instead of passing \"\""],"tags":["cli","validation","empty-value","path"],"backgroundTag":"empty-required-field","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"}