{"record":{"id":"eee70ed12d544b12","repo":"astrid-runtime/astrid","slug":"capsule-source-symlink-does-not-resolve-to-a-re","errorCode":null,"errorMessage":"capsule source symlink {} does not resolve to a regular file","messagePattern":"capsule source symlink (.+?) does not resolve to a regular file","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-capsule-install/src/storage.rs","lineNumber":798,"sourceCode":"            .to_path_buf();\n        let file_type = metadata.file_type();\n        if file_type.is_symlink() {\n            let resolved = fs::canonicalize(&name).with_context(|| {\n                format!(\"canonicalize capsule source symlink {}\", relative.display())\n            })?;\n            let canonical_root = fs::canonicalize(root)\n                .with_context(|| format!(\"canonicalize capsule source root {}\", root.display()))?;\n            if !resolved.starts_with(&canonical_root) {\n                bail!(\n                    \"capsule source symlink {} resolves outside source root\",\n                    relative.display()\n                );\n            }\n            let resolved_metadata = fs::metadata(&resolved).with_context(|| {\n                format!(\"stat capsule source symlink target {}\", relative.display())\n            })?;\n            if !resolved_metadata.is_file() {\n                bail!(\n                    \"capsule source symlink {} does not resolve to a regular file\",\n                    relative.display()\n                );\n            }\n            // File links are materialized as regular archive entries. This\n            // preserves npm's node_modules/.bin links without ever storing a\n            // redirect in the durable package.\n            entries.push((relative, resolved_metadata));\n            continue;\n        }\n        if file_type.is_dir() {\n            if relative.file_name().and_then(|name| name.to_str()) == Some(\".git\")\n                || relative.file_name().and_then(|name| name.to_str()) == Some(\"target\")\n            {\n                continue;\n            }\n            // Directory entries are included so empty directories survive\n            // archive round-trips; their descendants are sorted recursively.","sourceCodeStart":780,"sourceCodeEnd":816,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-capsule-install/src/storage.rs#L780-L816","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: symlink to a directory\ncurrent -> versions/v2\n\n// after: archive the files themselves\ncurrent/** (materialized regular files from versions/v2)","handlingStrategy":"validation","validationCode":"fn links_to_regular_file(link: &Path) -> std::io::Result<bool> {\n    Ok(std::fs::metadata(link)?.is_file())\n}","typeGuard":"fn is_file_link(link: &Path) -> bool {\n    std::fs::symlink_metadata(link).map_or(false, |m| m.file_type().is_symlink())\n        && std::fs::metadata(link).map_or(false, |m| m.is_file())\n}","tryCatchPattern":"if let Err(e) = publish(...) {\n    if e.to_string().contains(\"does not resolve to a regular file\") {\n        eprintln!(\"replace directory/special symlinks with real files before publishing\");\n    }\n}","preventionTips":["Never symlink directories inside a capsule source","Materialize version-switching links before archiving","Remove links to sockets/FIFOs from source trees"],"tags":["symlink","archive-validation","capsule"],"backgroundTag":"incompatible-source-type","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}