{"record":{"id":"af04cc6734c15fdf","repo":"can1357/oh-my-pi","slug":"target-0-not-a-directory","errorCode":null,"errorMessage":"target {0}: Not a directory","messagePattern":"target (.+?): Not a directory","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/pi-builtins/src/mv.rs","lineNumber":65,"sourceCode":"\tHardlinkGroupScanner, HardlinkOptions, HardlinkTracker, create_hardlink_context,\n\twith_optional_hardlink_context,\n};\n\n#[derive(Debug, Error)]\nenum MvError {\n\t#[error(\"cannot stat {0}: No such file or directory\")]\n\tNoSuchFile(String),\n\t#[error(\"cannot stat {0}: Not a directory\")]\n\tCannotStatNotADirectory(String),\n\t#[error(\"{0} and {1} are the same file\")]\n\tSameFile(String, String),\n\t#[error(\"cannot move {0} to a subdirectory of itself, {1}\")]\n\tSelfTargetSubdirectory(String, String),\n\t#[error(\"cannot overwrite directory {0} with non-directory\")]\n\tDirectoryToNonDirectory(String),\n\t#[error(\"cannot overwrite non-directory {1} with directory {0}\")]\n\tNonDirectoryToDirectory(String, String),\n\t#[error(\"target {0}: Not a directory\")]\n\tNotADirectory(String),\n\t#[error(\"target directory {0}: Not a directory\")]\n\tTargetNotADirectory(String),\n\t#[error(\"failed to access {0}: Not a directory\")]\n\tFailedToAccessNotADirectory(String),\n}\n\n#[derive(Debug, Error)]\nenum MvFailure {\n\t#[error(transparent)]\n\tMove(#[from] MvError),\n\t#[error(transparent)]\n\tIo(#[from] io::Error),\n\t#[error(\"{0}\")]\n\tMessage(String),\n}\n\ntype MvResult<T> = Result<T, MvFailure>;","sourceCodeStart":47,"sourceCodeEnd":83,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/crates/pi-builtins/src/mv.rs#L47-L83","documentation":"This is MvError::NotADirectory from crates/pi-builtins/src/mv.rs. Raised in `move_files_into_dir` (mv.rs:785-787) when the multi-operand form of mv resolves its target directory and finds it is not a directory: `mv src... target_dir` or `mv -t DIR src...` with a non-directory final operand. In GNU mv, a non-existent destination directory is simply created only for a single rename; for the many-to-one form the last operand must already be a directory, so the builtin reports this error instead.","triggerScenarios":"`mv a b c dest` where dest does not exist or is a regular file; `mv -t somefile src1 src2` where somefile is not a directory; `mv -t nonexistent src` (target directory never created); a symlink as target whose link target is a file.","commonSituations":"Batch-move scripts where the destination directory was never `mkdir -p`'d; typos in the directory name so it resolves to nothing; build scripts expecting a directory that a previous step failed to create; glob expansion producing a file as the final argument.","solutions":["Create the target directory before the move: `mkdir -p dest`.","Verify the last operand is the intended directory (`ls -ld dest`) and fix typos.","If you actually want a rename (single source to a new name), use the two-operand form `mv src dest` rather than many-to-one.","Resolve any symlink standing in for the target and point the command at the real directory."],"exampleFix":"// before (fails: backups is not a directory)\nmv file1 file2 backups\n\n// after\nmkdir -p backups && mv file1 file2 backups/","handlingStrategy":"validation","validationCode":"import * as fs from \"node:fs/promises\";\n\nexport async function ensureTargetDir(dir: string): Promise<string> {\n  const st = await fs.stat(dir).catch(() => null);\n  if (!st?.isDirectory()) {\n    await fs.mkdir(dir, { recursive: true });\n  }\n  return dir;\n}\n// await ensureTargetDir(dest); then mv src... dest","typeGuard":null,"tryCatchPattern":"try {\n  await runBuiltin(\"mv\", [...sources, dest]);\n} catch (e) {\n  if (String(e).startsWith(\"target \") && String(e).includes(\"Not a directory\")) {\n    await fs.mkdir(dest, { recursive: true });\n    await runBuiltin(\"mv\", [...sources, dest]); // retry once\n  } else throw e;\n}","preventionTips":["Always mkdir -p the destination directory before a many-to-one mv.","Keep the target directory as an explicit, verified variable, not a trailing glob result.","Check for typos: a misspelled directory silently resolves to 'not a directory'.","Remember mv only auto-creates the destination for a single-source rename, never for multiple sources."],"tags":["mv","filesystem","missing-directory","invalid-target"],"backgroundTag":"target-not-a-directory","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}