{"record":{"id":"222cf7c89ab505bd","repo":"can1357/oh-my-pi","slug":"cannot-overwrite-non-directory-1-with-directory","errorCode":null,"errorMessage":"cannot overwrite non-directory {1} with directory {0}","messagePattern":"cannot overwrite non-directory (.+?) with directory (.+?)","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/pi-builtins/src/mv.rs","lineNumber":63,"sourceCode":"#[cfg(unix)]\nuse self::hardlink::{\n\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}","sourceCodeStart":45,"sourceCodeEnd":81,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/crates/pi-builtins/src/mv.rs#L45-L81","documentation":"This is MvError::NonDirectoryToDirectory in crates/pi-builtins/src/mv.rs. Raised when the source is a directory (or non-file operand) and the destination already exists as a non-directory file — mv cannot replace a regular file with a directory. In `handle_two_paths` (mv.rs:626-641) this fires when `target_fs.exists() && source_is_dir` and the destination is not a directory, after the overwrite-mode prompt (declining an `-i` prompt also surfaces here). Format arguments: {0} is the source, {1} the destination.","triggerScenarios":"`mv srcdir existing_file` where existing_file is a regular file or symlink-to-file; `mv -T srcdir existing_file`; multiple sources with a file as the final target directory operand; answering 'n' to an interactive overwrite prompt in this same branch.","commonSituations":"A path that used to be a directory was replaced by a symlink or file (stale checkout, tooling change); typos where the last argument is a file instead of the intended target directory; scripts that expected `dest/` to exist and got a file of the same name; package managers leaving a file where a directory is expected.","solutions":["Remove or rename the existing file first (`rm dest` or `mv dest dest.bak`) if the directory should replace it, then re-run mv.","Verify operand order and target type: `file dest` / `ls -ld` to confirm the last argument is a directory.","If the destination was meant to be a target directory, create it (`mkdir -p dest`) and move into it.","Check for a symlink at the destination with `readlink dest` and correct or remove it."],"exampleFix":"// before (fails: dest is a regular file)\nmv srcdir dest\n\n// after\ndest_is_file && { mv dest dest.old; }\nmv srcdir dest   # or: mkdir -p dest && mv srcdir dest/srcdir","handlingStrategy":"validation","validationCode":"import * as fs from \"node:fs/promises\";\n\nexport async function moveDirIntoOrReplace(src: string, dest: string): Promise<void> {\n  const srcIsDir = (await fs.stat(src)).isDirectory();\n  const destSt = await fs.lstat(dest).catch(() => null);\n  if (srcIsDir && destSt && !destSt.isDirectory()) {\n    // decide explicitly: remove, back up, or pick a new destination\n    await fs.rename(dest, `${dest}.old`);\n  }\n  // proceed with mv\n}","typeGuard":null,"tryCatchPattern":"try {\n  await runBuiltin(\"mv\", [src, dest]);\n} catch (e) {\n  if (String(e).includes(\"cannot overwrite non-directory\")) {\n    logger.error(\"destination exists as a non-directory\", { src, dest });\n  } else throw e;\n}","preventionTips":["Confirm the last operand of a multi-source mv is an existing directory.","Back up or remove a stale file at the destination before moving a directory there.","Check for symlinks at destinations — a file symlink trips this error.","Verify operand order in scripts when paths come from variables or globs."],"tags":["mv","filesystem","type-mismatch","overwrite"],"backgroundTag":"mv-directory-over-file","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}