can1357/oh-my-pi · error
cannot stat {0}: Not a directory
Error message
cannot stat {0}: Not a directory What it means
GNU-compatible `mv` error (`MvError::CannotStatNotADirectory`, crates/pi-builtins/src/mv.rs:55). Same underlying condition as `NoSuchFile` — `symlink_metadata` failed on the source — but selected when the source operand ends with a trailing path separator (`path_ends_with_terminator`). The trailing `/` asserts the operand must be a directory, and stat on `foo/` fails with ENOTDIR rather than ENOENT on most platforms.
Source
Thrown at crates/pi-builtins/src/mv.rs:55
fs::{
MissingHandling, ResolveMode, are_hardlinks_or_one_way_symlink_to_same_file,
are_hardlinks_to_same_file, canonicalize, path_ends_with_terminator,
},
update_control::{self, UpdateMode},
};
use crate::host::{Host, Utility, format_usage, matches_parser, util};
#[cfg(unix)]
use self::hardlink::{
HardlinkGroupScanner, HardlinkOptions, HardlinkTracker, create_hardlink_context,
with_optional_hardlink_context,
};
#[derive(Debug, Error)]
enum MvError {
#[error("cannot stat {0}: No such file or directory")]
NoSuchFile(String),
#[error("cannot stat {0}: Not a directory")]
CannotStatNotADirectory(String),
#[error("{0} and {1} are the same file")]
SameFile(String, String),
#[error("cannot move {0} to a subdirectory of itself, {1}")]
SelfTargetSubdirectory(String, String),
#[error("cannot overwrite directory {0} with non-directory")]
DirectoryToNonDirectory(String),
#[error("cannot overwrite non-directory {1} with directory {0}")]
NonDirectoryToDirectory(String, String),
#[error("target {0}: Not a directory")]
NotADirectory(String),
#[error("target directory {0}: Not a directory")]
TargetNotADirectory(String),
#[error("failed to access {0}: Not a directory")]
FailedToAccessNotADirectory(String),
}
#[derive(Debug, Error)]View on GitHub (pinned to 9690622007)
Solutions
- Check whether the path exists: `ls -ld foo` — if missing, create it or fix the variable that supplies it.
- If the operand is actually a regular file, drop the trailing slash: `mv foo bar` instead of `mv foo/ bar`.
- Guard directory operands: `[ -d "$src" ] && mv "$src/" "$dst"`.
- Strip accidental trailing separators in scripts: `src="${src%/}"`.
Example fix
# before
mv "$build_dir/" out/ # build_dir unset => mv / out/ -> cannot stat '/': Not a directory
# after
: "${build_dir:?build_dir not set}"
[ -d "$build_dir" ] && mv "$build_dir" out/ Defensive patterns
Strategy: validation
Validate before calling
src="${src%/}" # drop trailing separator
[ -d "$src" ] || { echo "mv: '$src' is not a directory" >&2; exit 1; } Try / catch
if ! mv "$src" "$dst" 2>err.txt; then cat err.txt >&2; grep -q 'Not a directory' err.txt && exit 1; fi
Prevention
- Strip trailing slashes from directory variables (`${var%/}`).
- Only append `/` to operands you have verified are directories ([ -d ... ]).
- Remember a trailing `/` requires the operand to exist as a directory — a plain file or missing path fails stat.
- Expand and echo the final path (set -x) to catch empty variables that leave a lone `/`.
When it happens
Trigger: `mv foo/ bar` where `foo` does not exist (or is a regular file, so `foo/` cannot name a directory) — the trailing slash triggers this variant in `handle_two_paths`.
Common situations: Tab-completed or copy-pasted paths that keep a trailing `/` after the target was renamed or deleted; scripts appending `/` to a directory variable that is empty or points at a file; shell scripts written expecting `foo/` to still resolve when `foo` is a symlink later removed.
Related errors
- cannot stat {0}: No such file or directory
- {0} and {1} are the same file
- cannot move {0} to a subdirectory of itself, {1}
- cannot overwrite directory {0} with non-directory
- cannot overwrite non-directory {1} with directory {0}
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/ea5b27f204b471ad.
Report an issue: GitHub.