astrid-runtime/astrid · error
capsule archive contains a link or special file {name}
Error message
capsule archive contains a link or special file {name} What it means
When digesting a capsule archive, each entry's tar entry type is checked: only directories and regular files are allowed. Symlinks, hardlinks, and special files (devices, fifos) are rejected because the canonical archive format does not support them and links could redirect reads outside the intended tree during unpacking.
Source
Thrown at crates/astrid-capsule-install/src/source_digest.rs:58
let mut entry = entry.context("read capsule archive entry")?;
let path = entry.path().context("read capsule archive path")?;
if path.is_absolute()
|| path
.components()
.any(|component| matches!(component, std::path::Component::ParentDir))
{
bail!("capsule archive contains an unsafe path {}", path.display());
}
let name = path
.to_str()
.ok_or_else(|| anyhow::anyhow!("capsule archive path is not UTF-8"))?
.replace('\\', "/");
if !names.insert(name.clone()) {
bail!("capsule archive contains duplicate path {name}");
}
let entry_type = entry.header().entry_type();
if !entry_type.is_dir() && !entry_type.is_file() {
bail!("capsule archive contains a link or special file {name}");
}
let destination = staging.path().join(&path);
if entry_type.is_dir() {
fs::create_dir_all(&destination)
.with_context(|| format!("create capsule archive directory {name}"))?;
continue;
}
if let Some(parent) = destination.parent() {
fs::create_dir_all(parent)
.with_context(|| format!("create capsule archive parent for {name}"))?;
}
entry
.unpack(&destination)
.with_context(|| format!("unpack capsule archive file {name}"))?;
// Drain the entry explicitly so malformed/truncated streams fail
// before the canonical builder reads the staged tree.
let mut sink = Vec::new();
entryView on GitHub (pinned to affd8760f4)
Solutions
- Dereference symlinks when packing: `tar -czhf capsule.tgz -C <root> .` (or remove the symlinks and copy real files).
- Find offending entries with `tar -tvzf capsule.tgz | grep -E '^l|^[^d-]'` and remove or replace them.
- Exclude non-file artifacts from the archive (build sockets, fifos) via --exclude.
- Hard-code a packaging step that copies the tree (cp -rL) before archiving.
Example fix
// before: symlink left in tree, archive keeps the link entry ln -s ../vendor/wit wit; tar -czf capsule.tgz . // after: dereference links into real files tar -czhf capsule.tgz -C capsule-root .
Defensive patterns
Strategy: validation
Validate before calling
let out = std::process::Command::new("tar")
.args(["-tvzf", archive_path])
.output()?;
let has_links = String::from_utf8_lossy(&out.stdout)
.lines()
.any(|l| l.starts_with('l') || l.starts_with('h')); Type guard
fn only_dirs_and_files(types: &[tar::EntryType]) -> bool {
types.iter().all(|t| t.is_dir() || t.is_file())
} Try / catch
match archive_digest_for_source(archive) {
Err(e) if e.to_string().contains("link or special file") => {
anyhow::bail!("repack with dereferenced symlinks: tar -czhf capsule.tgz -C <root> .");
}
other => other,
} Prevention
- Use `tar -h` (dereference) or `cp -rL` before archiving trees containing symlinks
- Exclude build artifacts (sockets, fifos) from capsule archives
- Avoid hardlinks in capsule packaging; ship regular files
- Lint the source tree for symlinks before publishing
When it happens
Trigger: Calling archive_digest_for_source on a .tar.gz containing a symlink, hardlink, or special-file entry — e.g. archives built with `tar -h` disabled over trees containing symlinks, or archives capturing dev/fifo nodes.
Common situations: Project trees containing symlinks (node_modules links, vendored deps linked in) archived without dereferencing; archives made from a build directory containing sockets/fifos; hand-crafted archives with hardlinks for space savings.
Understand the failure class
Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.
Related errors
- durable capsule archive contains a link or special file
- capsule archive contains an unsafe path {}
- durable capsule archive contains unsafe path
- malicious shuttle detected: links are not allowed ('{}')
- authenticated AstridFS.app is redirected or not a directory
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/2cbf2b8234415edb.
Report an issue: GitHub.