astrid-runtime/astrid · error
unsafe archive path '{path}'
Error message
unsafe archive path '{path}' What it means
`validate_archive_path` rejects archive paths that are absolute or contain `..` components, preventing capsules from being written to arbitrary filesystem locations. Only relative, traversal-free paths are accepted.
Source
Thrown at crates/astrid-cli/src/commands/distro/shuttle.rs:258
/// The archive-relative path of a capsule member: `capsules/<name>.capsule`.
pub(crate) fn capsule_member_path(name: &str) -> String {
format!("{CAPSULES_DIR}/{name}.capsule")
}
/// The on-disk path of a capsule inside an unpacked mirror.
pub(crate) fn capsule_mirror_path(mirror: &Path, name: &str) -> PathBuf {
mirror.join(CAPSULES_DIR).join(format!("{name}.capsule"))
}
/// Reject archive paths that are absolute, contain `..`, or are empty.
fn validate_archive_path(path: &str) -> anyhow::Result<()> {
if path.is_empty() {
bail!("empty archive path");
}
let p = Path::new(path);
if p.is_absolute() || p.components().any(|c| matches!(c, Component::ParentDir)) {
bail!("unsafe archive path '{path}'");
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
fn sample_entries() -> Vec<ShuttleEntry> {
vec![
ShuttleEntry {
path: SIG_NAME.to_string(),
content: ShuttleContent::Bytes(b"deadbeef".to_vec()),
},
ShuttleEntry {
path: MANIFEST_NAME.to_string(),
content: ShuttleContent::Bytes(b"schema-version = 1\n".to_vec()),
},View on GitHub (pinned to affd8760f4)
Solutions
- Use a relative path without `..`, e.g. `--out my.capsule` or `--out out/my.capsule`.
- `cd` to the intended output directory and pass a bare filename.
- Omit the path so pack writes to the default mirror location `<name>.capsule`.
Example fix
// before $ astrid distro pack --out /tmp/x.capsule // after $ cd /tmp && astrid distro pack --out x.capsule
Defensive patterns
Strategy: validation
Validate before calling
fn archive_path_is_safe(p: &str) -> bool {
let path = std::path::Path::new(p);
!path.is_absolute()
&& !path.components().any(|c| matches!(c, std::path::Component::ParentDir))
} Type guard
fn is_safe_out_path(p: &str) -> bool {
let q = Path::new(p);
q.is_relative() && !q.components().any(|c| matches!(c, Component::ParentDir))
} Try / catch
match pack(&staging, out) {
Err(e) if e.to_string().contains("unsafe archive path") => {
eprintln!("use a relative path without '..': {e}");
}
other => other?,
} Prevention
- Pass bare relative filenames for --out
- cd into the target directory instead of using absolute paths
- Never interpolate user input directly into the output path
When it happens
Trigger: Calling `pack` with an output path like `/tmp/foo.capsule` or `../foo.capsule`; `validate_archive_path` bails before the archive is created.
Common situations: Users passing an absolute path out of habit (`--out $(pwd)/x.capsule`); scripts built on a different machine using `..`-relative paths; path built by string concatenation from user input.
Understand the failure class
Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.
Related errors
- empty archive path
- Distro.toml has no parent directory
- Invalid session ID (must be a UUID): {id}
- refusing to seed canonical from a non-content-address contra
- capsule source is neither a directory nor a regular file: {}
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/688b5289f68d8c4f.
Report an issue: GitHub.