astrid-runtime/astrid · error
capsule source is neither a directory nor a regular file: {}
Error message
capsule source is neither a directory nor a regular file: {} What it means
canonical_archive_for_source computes a registry archive digest for a local capsule source. It accepts a directory (canonicalized via canonical_capsule_archive) or a regular file treated as a gzipped tar archive. If the path is neither (missing path, symlink target that vanished, fifo, device, etc.), it bails because there is no defined way to digest that kind of source.
Source
Thrown at crates/astrid-capsule-install/src/source_digest.rs:27
/// Compute the registry archive digest for a local capsule source.
///
/// Directories are canonicalized using the same deterministic archive builder
/// used by durable publication. Archive files are unpacked into a fresh
/// temporary directory, validated for safe regular-file/directory entries,
/// then canonicalized through that same builder. No principal store is opened
/// and no durable state is mutated.
pub fn archive_digest_for_source(source: &Path) -> anyhow::Result<String> {
let archive = canonical_archive_for_source(source)?;
Ok(blake3::hash(&archive).to_hex().to_string())
}
fn canonical_archive_for_source(source: &Path) -> anyhow::Result<Vec<u8>> {
if source.is_dir() {
return crate::storage::canonical_capsule_archive(source);
}
if !source.is_file() {
bail!(
"capsule source is neither a directory nor a regular file: {}",
source.display()
);
}
let staging = tempfile::tempdir().context("create source digest staging directory")?;
let file = fs::File::open(source)
.with_context(|| format!("open capsule archive {}", source.display()))?;
let decoder = flate2::read::GzDecoder::new(file);
let mut archive = tar::Archive::new(decoder);
let mut names = BTreeSet::new();
for entry in archive.entries().context("read capsule archive entries")? {
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))View on GitHub (pinned to affd8760f4)
Solutions
- Verify the path exists and is a directory or regular file: `ls -la <path>` / `test -d <path> || test -f <path>`.
- Fix the path (typos, correct working directory) and re-run the digest command.
- If the source is an archive, ensure it is a regular .tar.gz file, not a named pipe or other special file.
- If digesting a directory, pass the directory itself rather than a symlink to it.
Example fix
// before
archive_digest_for_source(Path::new("./capsule.tgz")) // path is actually a fifo
// after: ensure a real regular file
assert!(Path::new("./capsule.tgz").is_file());
archive_digest_for_source(Path::new("./capsule.tgz")) Defensive patterns
Strategy: validation
Validate before calling
fn validate_capsule_source(source: &Path) -> std::io::Result<()> {
let md = std::fs::metadata(source)?; // follows symlinks; errors if missing
if !(md.is_dir() || md.is_file()) {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
format!("capsule source must be a directory or regular file: {}", source.display()),
));
}
Ok(())
} Type guard
fn is_valid_source(source: &Path) -> bool {
source.is_dir() || source.is_file()
} Try / catch
match archive_digest_for_source(path) {
Err(e) if e.to_string().contains("neither a directory nor a regular file") => {
anyhow::bail!("check the --source path: {} does not exist or is a special file", path.display());
}
other => other,
} Prevention
- Check `test -f`/`test -d` on the source path before running digest/publish commands
- Run commands from the intended working directory when using relative paths
- Never pass named pipes, sockets, or device nodes as capsule sources
- Verify the file still exists after any pre-processing step that may move it
When it happens
Trigger: Calling archive_digest_for_source with a path that does not exist, points at a broken symlink, is a fifo/socket/device node, or (on some platforms) a symlink whose target type cannot be determined via Path::is_dir/is_file.
Common situations: Typo'd path passed to a publish/digest command; source deleted or moved before digesting; a Unix socket or fifo mistakenly passed as the capsule source; running from a different working directory so a relative path no longer resolves.
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
- mountpoint is not a directory: {}
- InvalidInput
- Astrid volume is not a regular file
- legacy env/secret path is not a regular directory: {}
- Source path does not exist: {source}
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/bdcec2449c665adb.
Report an issue: GitHub.