astrid-runtime/astrid · error
capsule source symlink
Error message
capsule source symlink {} does not resolve to a regular file What it means
Raised by collect_entries when a symlink in the capsule source resolves inside the source root but its target is not a regular file (e.g. a directory, FIFO, or device). Only symlinks to regular files are accepted; they are materialized as regular archive entries (preserving node_modules/.bin semantics).
Solutions
- Point the symlink at a regular file, or remove the link and copy the directory contents in as real files.
- For directory-style links (version switching), resolve the desired version's files directly into the tree before archiving.
- Remove any links to sockets/FIFOs from the capsule directory.
Example fix
// before: symlink to a directory current -> versions/v2 // after: archive the files themselves current/** (materialized regular files from versions/v2)
Defensive patterns
Strategy: validation
Validate before calling
fn links_to_regular_file(link: &Path) -> std::io::Result<bool> {
Ok(std::fs::metadata(link)?.is_file())
} Type guard
fn is_file_link(link: &Path) -> bool {
std::fs::symlink_metadata(link).map_or(false, |m| m.file_type().is_symlink())
&& std::fs::metadata(link).map_or(false, |m| m.is_file())
} Try / catch
if let Err(e) = publish(...) {
if e.to_string().contains("does not resolve to a regular file") {
eprintln!("replace directory/special symlinks with real files before publishing");
}
} Prevention
- Never symlink directories inside a capsule source
- Materialize version-switching links before archiving
- Remove links to sockets/FIFOs from source trees
When it happens
Trigger: Archiving a capsule containing a symlink to a directory or special file within the tree — e.g. `current -> ./versions/v2/` or a link to a socket — where the resolved fs::metadata().is_file() check fails.
Common situations: Directory links used for version switching inside the capsule; links to pipes/sockets left by tooling; linking a whole vendored directory instead of individual files.
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
- legacy capsule contains symlink
- capsule materialization destination is not a directory
- capsule projection contains a symbolic link
- capsule source contains unsupported special file
- capsule source symlink
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/eee70ed12d544b12.
Report an issue: GitHub.
Appendix: source
Thrown at crates/astrid-capsule-install/src/storage.rs:798
.to_path_buf();
let file_type = metadata.file_type();
if file_type.is_symlink() {
let resolved = fs::canonicalize(&name).with_context(|| {
format!("canonicalize capsule source symlink {}", relative.display())
})?;
let canonical_root = fs::canonicalize(root)
.with_context(|| format!("canonicalize capsule source root {}", root.display()))?;
if !resolved.starts_with(&canonical_root) {
bail!(
"capsule source symlink {} resolves outside source root",
relative.display()
);
}
let resolved_metadata = fs::metadata(&resolved).with_context(|| {
format!("stat capsule source symlink target {}", relative.display())
})?;
if !resolved_metadata.is_file() {
bail!(
"capsule source symlink {} does not resolve to a regular file",
relative.display()
);
}
// File links are materialized as regular archive entries. This
// preserves npm's node_modules/.bin links without ever storing a
// redirect in the durable package.
entries.push((relative, resolved_metadata));
continue;
}
if file_type.is_dir() {
if relative.file_name().and_then(|name| name.to_str()) == Some(".git")
|| relative.file_name().and_then(|name| name.to_str()) == Some("target")
{
continue;
}
// Directory entries are included so empty directories survive
// archive round-trips; their descendants are sorted recursively.View on GitHub (pinned to affd8760f4)