denoland/deno · error
Refusing to include {}: not a regular file (symlinks and spe
Error message
Refusing to include {}: not a regular file (symlinks and special files are excluded) What it means
While collecting auto-included files (README/LICENSE at cli/tools/pack/mod.rs:391 and :413), `read_auto_included_file` uses `symlink_metadata` and refuses anything that is not a regular file. Symlinks and special files (FIFOs, devices) are excluded so the tarball cannot smuggle content from outside the package directory.
Source
Thrown at cli/tools/pack/mod.rs:374
}
/// Read an auto-included file (README/LICENSE) only if it is a regular
/// file in the package directory. We use `symlink_metadata` rather than
/// `Path::exists()` + `read()` so a symlink pointing outside the
/// package — e.g. a `LICENSE` symlink to `~/.ssh/id_rsa` — never gets
/// packed. Returns `Ok(None)` if the path does not exist or is not a
/// regular file; returns `Err` only on actual I/O failure when reading
/// a confirmed regular file.
fn read_auto_included_file(
path: &std::path::Path,
) -> Result<Option<Vec<u8>>, AnyError> {
let metadata = match std::fs::symlink_metadata(path) {
Ok(m) => m,
Err(e) if e.kind() == std::io::ErrorKind::NotFound => return Ok(None),
Err(e) => return Err(e.into()),
};
if !metadata.file_type().is_file() {
bail!(
"Refusing to include {}: not a regular file (symlinks and special files are excluded)",
path.display()
);
}
Ok(Some(std::fs::read(path)?))
}
fn collect_readme_license_files(
package: &JsrPackageConfig,
) -> Result<Vec<ReadmeOrLicense>, AnyError> {
let package_dir = package.config_file.dir_path();
let mut files = Vec::new();
// Look for README files (case-insensitive)
for name in &["README.md", "README", "readme.md", "Readme.md", "readme"] {
let path = package_dir.join(name);
if let Some(content) = read_auto_included_file(&path)? {
files.push(ReadmeOrLicense {View on GitHub (pinned to 9ad36f7a2c)
Solutions
- Replace the symlink with a real copy of the file (`cp -L` then remove the link).
- Generate the LICENSE/README per package at build time instead of linking.
- If the file is not meant to ship, delete or rename it so auto-include skips it (missing files return Ok(None)).
Example fix
# before LICENSE -> ../../LICENSE deno pack # after cp -L LICENSE LICENSE.tmp && mv -f LICENSE.tmp LICENSE deno pack
Defensive patterns
Strategy: validation
Validate before calling
// Refuse to pack when README/LICENSE is a symlink
import { lstatSync } from "node:fs";
for (const f of ["README.md", "LICENSE"]) {
try {
const st = lstatSync(f);
if (!st.isFile()) {
console.error(`${f} is not a regular file; replace symlink before packing`);
process.exit(1);
}
} catch { /* absent files are fine */ }
} Type guard
import { lstatSync } from "node:fs";
const isRegularFile = (p: string): boolean => {
try { return lstatSync(p).isFile(); } catch { return false; }
}; Prevention
- Don't symlink shared LICENSE files into member packages; copy them.
- Check `ls -l README* LICENSE*` in release scripts before packing.
- Avoid dotfile managers that materialize READMEs as symlinks inside packages.
When it happens
Trigger: README.md, README.md/LICENSE or LICENSE being a symlink (common in monorepo roots pointing at a shared LICENSE). A named pipe or other special file where README/LICENSE is expected.
Common situations: Workspaces that symlink a root LICENSE into each member; dotfiles managers (stow) that symlink READMEs; CI checkouts that materialize symlinks. True hardlinks are fine (still regular files), which is why only symlink/special cases hit it.
Related errors
- ERR_FS_INVALID_SYMLINK_TYPE
- ERR_INCOMPATIBLE_OPTION_PAIR
- Source and destination paths refer to the same file
- On Windows the target must be a file or directory
- On Windows an `options` argument is required if the target d
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/451d082f736ce7a7.
Report an issue: GitHub.