{"record":{"id":"d377dcd0886b4ba9","repo":"can1357/oh-my-pi","slug":"2","errorCode":"2","errorMessage":"target {} is not a directory","messagePattern":"target (.+?) is not a directory","errorType":"error_code","errorClass":"LnError","httpStatus":null,"severity":"error","filePath":"crates/pi-builtins/src/ln.rs","lineNumber":53,"sourceCode":"\tsymbolic:       bool,\n\trelative:       bool,\n\tlogical:        bool,\n\ttarget_dir:     Option<PathBuf>,\n\tno_target_dir:  bool,\n\tno_dereference: bool,\n\tverbose:        bool,\n}\n\n#[derive(Clone, Debug, Eq, PartialEq)]\nenum OverwriteMode {\n\tNoClobber,\n\tInteractive,\n\tForce,\n}\n\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}\")]","sourceCodeStart":35,"sourceCodeEnd":71,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/crates/pi-builtins/src/ln.rs#L35-L71","documentation":"LnError::TargetIsNotADirectory is thrown by the ln builtin when link creation is requested inside a target path that exists but is not a directory — i.e., the caller asked for links to be placed in a directory (form `ln source... target-dir/`) but the target is a regular file or other non-directory. It carries the offending PathBuf, shell-quoted in the message, and surfaces with OS error code 2 (InvalidInput-class io error).","triggerScenarios":"Calling the ln builtin with multiple sources and a final target argument that resolves to an existing non-directory file (e.g. `ln a b c existing-file` expecting 'existing-file' to be a directory); creating a hard/symbolic link where the destination directory component is actually a file.","commonSituations":"Typos where the intended directory name collides with an existing file name; scripts assuming a directory exists but a file was created there earlier; passing a symlink to a file as the target directory.","solutions":["Verify the target path is a directory (ls -ld target) or create it with mkdir -p before linking","Remove or rename the non-directory file occupying the target path","Pass a single source when creating a named link instead of the directory-target form","If the path is a symlink, ensure it resolves to a directory"],"exampleFix":"// before\nln([\"a.txt\", \"b.txt\", \"dest\"], {}); // dest is a regular file\n// after\nfs::create_dir_all(\"dest\");\nln([\"a.txt\", \"b.txt\", \"dest\"], {});","handlingStrategy":"validation","validationCode":"fn ensure_dir_target(target: &std::path::Path) -> std::io::Result<()> {\n\tlet md = std::fs::metadata(target)?;\n\tif !md.is_dir() {\n\t\treturn Err(std::io::Error::new(\n\t\t\tstd::io::ErrorKind::InvalidInput,\n\t\t\tformat!(\"{} is not a directory\", target.display()),\n\t\t));\n\t}\n\tOk(())\n}","typeGuard":null,"tryCatchPattern":"match ln_result {\n\tErr(e) if e.to_string().contains(\"is not a directory\") => {\n\t\teprintln!(\"create or fix the target directory first: {e}\");\n\t}\n\tErr(e) => return Err(e),\n\tOk(()) => /* ... */,\n}","preventionTips":["Check metadata().is_dir() on multi-source link targets before invoking ln","mkdir -p the target directory in setup steps of scripts","Distinguish single-source named-link form from multi-source directory-target form in your call sites"],"tags":["rust","io","filesystem","symlink","thiserror"],"backgroundTag":"target-not-a-directory","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}