{"record":{"id":"84036038f6565287","repo":"can1357/oh-my-pi","slug":"target-directory-0-not-a-directory","errorCode":null,"errorMessage":"target directory {0}: Not a directory","messagePattern":"target directory (.+?): Not a directory","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/pi-builtins/src/mv.rs","lineNumber":67,"sourceCode":"};\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>;\n\n/// Parsed `mv` invocation.","sourceCodeStart":49,"sourceCodeEnd":85,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/crates/pi-builtins/src/mv.rs#L49-L85","documentation":"This is MvError::TargetNotADirectory from crates/pi-builtins/src/mv.rs. Raised up front in `run_matches` (mv.rs:381-385) when the explicit `-t/--target-directory` option is given but the resolved DIRECTORY operand is not a directory on the filesystem. Unlike the implicit last-operand form (NotADirectory), this check runs before any source is examined, so it is the earliest and clearest signal that the `-t` target is invalid.","triggerScenarios":"`mv -t somefile src1 src2` where somefile is a regular file or symlink-to-file; `mv --target-directory=does_not_exist src` (path never created); `-t` pointing at a deleted or renamed directory; scripts interpolating an empty or wrong variable into the -t value.","commonSituations":"Pipeline scripts where the target directory is produced by a previous command that failed silently; find/xargs-driven moves passing a stale directory path; users confusing `-t DIR` (move into DIR) with plain rename semantics; container images where the directory was expected from a mounted volume that failed to mount.","solutions":["Create the directory before invoking: `mkdir -p \"$(target)\"` then `mv -t \"$target\" sources...`.","Double-check the value passed to -t; it must be an existing directory, not a destination filename.","Verify mounts/variables that supply the path (`echo $target; ls -ld \"$target\"`).","If you meant a single rename, drop `-t` and use `mv src dest`."],"exampleFix":"// before (fails: $out is not an existing directory)\nmv -t \"$out\" *.log\n\n// after\nmkdir -p \"$out\" && mv -t \"$out\" *.log","handlingStrategy":"validation","validationCode":"import * as fs from \"node:fs/promises\";\n\nexport async function resolveTargetDir(dir: string): Promise<string> {\n  const st = await fs.stat(dir).catch(() => null);\n  if (!st) await fs.mkdir(dir, { recursive: true });\n  else if (!st.isDirectory()) throw new Error(`-t target ${dir} exists but is not a directory`);\n  return dir;\n}\n// const t = await resolveTargetDir(target); mv -t t sources...","typeGuard":null,"tryCatchPattern":"try {\n  await runBuiltin(\"mv\", [\"-t\", target, ...sources]);\n} catch (e) {\n  if (String(e).startsWith(\"target directory \") && String(e).includes(\"Not a directory\")) {\n    logger.error(\"--target-directory must be an existing directory\", { target });\n  } else throw e;\n}","preventionTips":["Validate the -t value with stat/isDirectory before constructing the command.","mkdir -p the target when its existence depends on a previous step or mount.","Don't confuse -t DIR (move into DIR) with the plain two-operand rename form.","Echo or log the expanded variable supplying -t in scripts to catch empty/renamed paths early."],"tags":["mv","filesystem","missing-directory","cli-flags"],"backgroundTag":"target-not-a-directory","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}