astrid-runtime/astrid · error
legacy capsule contains special file
Error message
legacy capsule contains special file {} What it means
Raised by copy_legacy_tree when a legacy capsule entry is neither a directory nor a regular file (e.g. a FIFO, socket, or device node). The library only copies regular files and directories into canonical legacy archives; anything else is rejected as unsafe/unsupported.
Solutions
- Remove or relocate the special file named in the message out of the capsule directory.
- Stop any process using the socket/FIFO inside the capsule tree and clean up before re-running the migration.
- Restore the capsule from a clean source that contains only regular files and directories.
Example fix
# before: stale socket inside the capsule capsule/runtime.sock # after: remove it rm capsule/runtime.sock
Defensive patterns
Strategy: validation
Validate before calling
fn has_special_files(dir: &Path) -> std::io::Result<bool> {
Ok(walkdir(dir).any(|e| !e.file_type().is_file() && !e.file_type().is_dir() && !e.file_type().is_symlink()))
} Type guard
fn is_fs_special(md: &std::fs::Metadata) -> bool {
!md.is_file() && !md.is_dir() && !md.is_symlink()
} Try / catch
match migrate(...) {
Err(e) if e.to_string().contains("special file") => { cleanup_runtime_artifacts(&dir)?; retry(); }
other => other?,
} Prevention
- Stop processes that create sockets/FIFOs in the capsule dir before archiving
- Clean runtime artifacts (e.g. *.sock) before migration
- Restore capsules from clean, regular-file-only backups
When it happens
Trigger: Migrating a legacy capsule tree that contains special filesystem objects — named pipes, unix sockets, device files — at any depth under the capsule directory.
Common situations: Capsule directory previously hosted a running process that left sockets/FIFOs behind; a bad backup/restore produced device nodes; users pointed a capsule dir at a mount point with odd entries.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- capsule source contains unsupported special file
- capsule projection contains a special file
- capsule source symlink
- legacy capsule contains symlink
- an incomplete capsule authority update exists at
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/661a1c330b42f035.
Report an issue: GitHub.
Appendix: source
Thrown at crates/astrid-capsule-install/src/storage.rs:711
fs::create_dir_all(destination)?;
for (path, metadata) in read_dir_sorted(source)? {
let relative = path
.file_name()
.ok_or_else(|| anyhow::anyhow!("legacy capsule entry has no name"))?;
let destination = destination.join(relative);
if metadata.file_type().is_symlink() {
bail!("legacy capsule contains symlink {}", path.display());
}
if metadata.is_dir() {
copy_legacy_tree(&path, &destination)?;
} else if metadata.is_file() {
let name = path.file_name().and_then(|name| name.to_str());
if matches!(name, Some("meta.json" | "authority.json" | ".env.json")) {
continue;
}
fs::copy(path, destination)?;
} else {
bail!("legacy capsule contains special file {}", path.display());
}
}
Ok(())
}
fn manifest_identity(source_dir: &Path) -> anyhow::Result<(String, String)> {
let manifest = fs::read_to_string(source_dir.join("Capsule.toml"))
.with_context(|| format!("read capsule manifest from {}", source_dir.display()))?;
let value: toml::Value = toml::from_str(&manifest).context("parse capsule manifest")?;
let id = value
.get("package")
.and_then(|package| package.get("name"))
.and_then(toml::Value::as_str)
.ok_or_else(|| anyhow::anyhow!("Capsule.toml has no package.name"))?;
let version = value
.get("package")
.and_then(|package| package.get("version"))
.and_then(toml::Value::as_str)View on GitHub (pinned to affd8760f4)