{"record":{"id":"0129f083be7073cc","repo":"can1357/oh-my-pi","slug":"hard-link-not-allowed-for-directory","errorCode":null,"errorMessage":"{}: hard link not allowed for directory","messagePattern":"(.+?): hard link not allowed for directory","errorType":"error_code","errorClass":"LnError","httpStatus":null,"severity":"error","filePath":"crates/pi-builtins/src/ln.rs","lineNumber":68,"sourceCode":"\n#[derive(Error, Debug)]\nenum LnError {\n\t#[error(\"target {} is not a directory\", _0.quote())]\n\tTargetIsNotADirectory(PathBuf),\n\n\t#[error(\"\")]\n\tSomeLinksFailed,\n\n\t#[error(\"{} and {} are the same file\", _0.quote(), _1.quote())]\n\tSameFile(PathBuf, PathBuf),\n\n\t#[error(\"missing destination file operand after {}\", _0.quote())]\n\tMissingDestination(PathBuf),\n\n\t#[error(\"extra operand {}\\nTry '{} --help' for more information.\", _0.quote(), _1)]\n\tExtraOperand(OsString, String),\n\n\t#[error(\"{}: hard link not allowed for directory\", _0.to_string_lossy())]\n\tFailedToCreateHardLinkDir(PathBuf),\n\n\t#[error(\"{0}\")]\n\tMessage(String),\n\n\t#[error(\"{0}\")]\n\tIo(#[from] std::io::Error),\n}\n\n\nmod options {\n\tpub const FORCE: &str = \"force\";\n\t//pub const DIRECTORY: &str = \"directory\";\n\tpub const INTERACTIVE: &str = \"interactive\";\n\tpub const NO_DEREFERENCE: &str = \"no-dereference\";\n\tpub const SYMBOLIC: &str = \"symbolic\";\n\tpub const LOGICAL: &str = \"logical\";\n\tpub const PHYSICAL: &str = \"physical\";","sourceCodeStart":50,"sourceCodeEnd":86,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/crates/pi-builtins/src/ln.rs#L50-L86","documentation":"The ln builtin's FailedToCreateHardLinkDir variant fires when a hard link to a directory is requested. Most filesystems and POSIX forbid link(2) on directories (only root may, and even then most kernels refuse with EPERM), so the tool rejects it early with the quoted directory path.","triggerScenarios":"Invoking `ln somedir somelink` (hard-link mode) where somedir is a directory and -s/--symbolic was not passed.","commonSituations":"Users intending a symlink but forgetting -s; scripts that portably link both files and dirs without branching on type.","solutions":["Use -s (symbolic link) for directories: `ln -s somedir somelink`","Branch on file type: only hard-link regular files","If a directory reference is needed, consider a symlink or bind mount instead"],"exampleFix":"// before\nln(&host, &[\"mydir\", \"mydir-link\"])?;\n// after\nln(&host, &[\"-s\", \"mydir\", \"mydir-link\"])?;","handlingStrategy":"validation","validationCode":"fn ensure_not_dir(p: &Path) -> std::io::Result<()> {\n    let md = std::fs::symlink_metadata(p)?;\n    if md.is_dir() {\n        return Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, format!(\"{}: hard link not allowed for directory\", p.display())));\n    }\n    Ok( )\n}","typeGuard":"fn is_regular_file(p: &Path) -> bool {\n    std::fs::symlink_metadata(p).map(|m| m.is_file()).unwrap_or(false)\n}","tryCatchPattern":"match ln(&host, args) {\n    Err(e) if e.to_string().contains(\"hard link not allowed for directory\") => {\n        // fall back to symlink\n        ln(&host, &[\"-s\", args[0], args[1]])?;\n    }\n    other => other?,\n}","preventionTips":["Check the source type before hard-linking; directories need symlinks","Remember link(2) is forbidden on directories on Linux/macOS regardless of privileges"],"tags":["filesystem","hardlink","directory","unsupported-operation"],"backgroundTag":"hard-link-not-allowed-for-directory","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}