{"record":{"id":"451d082f736ce7a7","repo":"denoland/deno","slug":"refusing-to-include-not-a-regular-file-symlin","errorCode":null,"errorMessage":"Refusing to include {}: not a regular file (symlinks and special files are excluded)","messagePattern":"Refusing to include (.+?): not a regular file \\(symlinks and special files are excluded\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cli/tools/pack/mod.rs","lineNumber":374,"sourceCode":"}\n\n/// Read an auto-included file (README/LICENSE) only if it is a regular\n/// file in the package directory. We use `symlink_metadata` rather than\n/// `Path::exists()` + `read()` so a symlink pointing outside the\n/// package — e.g. a `LICENSE` symlink to `~/.ssh/id_rsa` — never gets\n/// packed. Returns `Ok(None)` if the path does not exist or is not a\n/// regular file; returns `Err` only on actual I/O failure when reading\n/// a confirmed regular file.\nfn read_auto_included_file(\n  path: &std::path::Path,\n) -> Result<Option<Vec<u8>>, AnyError> {\n  let metadata = match std::fs::symlink_metadata(path) {\n    Ok(m) => m,\n    Err(e) if e.kind() == std::io::ErrorKind::NotFound => return Ok(None),\n    Err(e) => return Err(e.into()),\n  };\n  if !metadata.file_type().is_file() {\n    bail!(\n      \"Refusing to include {}: not a regular file (symlinks and special files are excluded)\",\n      path.display()\n    );\n  }\n  Ok(Some(std::fs::read(path)?))\n}\n\nfn collect_readme_license_files(\n  package: &JsrPackageConfig,\n) -> Result<Vec<ReadmeOrLicense>, AnyError> {\n  let package_dir = package.config_file.dir_path();\n  let mut files = Vec::new();\n\n  // Look for README files (case-insensitive)\n  for name in &[\"README.md\", \"README\", \"readme.md\", \"Readme.md\", \"readme\"] {\n    let path = package_dir.join(name);\n    if let Some(content) = read_auto_included_file(&path)? {\n      files.push(ReadmeOrLicense {","sourceCodeStart":356,"sourceCodeEnd":392,"githubUrl":"https://github.com/denoland/deno/blob/9ad36f7a2cce60488e6ec52283efb32efddaf93a/cli/tools/pack/mod.rs#L356-L392","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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))."],"exampleFix":"# before\nLICENSE -> ../../LICENSE\ndeno pack\n# after\ncp -L LICENSE LICENSE.tmp && mv -f LICENSE.tmp LICENSE\ndeno pack","handlingStrategy":"validation","validationCode":"// Refuse to pack when README/LICENSE is a symlink\nimport { lstatSync } from \"node:fs\";\nfor (const f of [\"README.md\", \"LICENSE\"]) {\n  try {\n    const st = lstatSync(f);\n    if (!st.isFile()) {\n      console.error(`${f} is not a regular file; replace symlink before packing`);\n      process.exit(1);\n    }\n  } catch { /* absent files are fine */ }\n}","typeGuard":"import { lstatSync } from \"node:fs\";\nconst isRegularFile = (p: string): boolean => {\n  try { return lstatSync(p).isFile(); } catch { return false; }\n};","tryCatchPattern":null,"preventionTips":["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."],"tags":["pack","filesystem","symlink","license"],"backgroundTag":"symlink-not-allowed","analyzedSha":"9ad36f7a2cce60488e6ec52283efb32efddaf93a","analyzedAt":"2026-08-20T13:07:44.778Z","contentChangedAt":"2026-08-20T13:07:44.778Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}