can1357/oh-my-pi · error

failed to access {0}: Not a directory

Error message

failed to access {0}: Not a directory

What it means

This error from the built-in `mv` utility (MvError::FailedToAccessNotADirectory) means the target path was treated as a directory, but some component of it is not a directory, so the move cannot be performed. It mirrors GNU coreutils' `mv: failed to access 'X': Not a directory` diagnostic. It is thrown when the source and target are both non-directories and the target operand ends with a path terminator (a trailing `/` or `/.`), signaling the user expects a directory that does not exist as one.

Source

Thrown at crates/pi-builtins/src/mv.rs:69

#[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)]
enum MvFailure {
	#[error(transparent)]
	Move(#[from] MvError),
	#[error(transparent)]
	Io(#[from] io::Error),
	#[error("{0}")]
	Message(String),
}

type MvResult<T> = Result<T, MvFailure>;

/// Parsed `mv` invocation.
pub(crate) struct Mv {
	matches: ArgMatches,

View on GitHub (pinned to 9690622007)

Solutions

  1. Create the destination directory first (mkdir -p dest) before running mv
  2. Remove the trailing slash from the target if you actually want to rename the source to that name
  3. Check with `ls -ld dest` whether the target exists and is a directory; fix the path if it is a file
  4. Use `mv -T source.txt dest` (no-target-directory) if you intentionally want to rename onto the plain name

Example fix

// before
mv results.txt build/out/
// error: failed to access 'build/out/': Not a directory
// after
mkdir -p build/out && mv results.txt build/out/
Defensive patterns

Strategy: validation

Validate before calling

import * as fs from "node:fs";
function ensureTargetDirUsable(target: string): boolean {
  if (!target.endsWith("/")) return true; // plain rename, no dir expectation
  const dir = target.replace(/\/+$/, "");
  return fs.existsSync(dir) && fs.statSync(dir).isDirectory();
}
if (!ensureTargetDirUsable(dest)) mkdirSync(dest, { recursive: true });

Try / catch

try {
  await run("mv", [src, dest]);
} catch (err) {
  if (String(err.stderr).includes("failed to access") && String(err.stderr).includes("Not a directory")) {
    await fs.mkdir(dest.replace(/\/+$/, ""), { recursive: true });
    await run("mv", [src, dest]);
  } else throw err;
}

Prevention

When it happens

Trigger: Running `mv source.txt dest/` (or `dest/.`) where `dest` does not exist or is a regular file, with the two-path handler in handle_two_paths (mv.rs:598-604): the target ends with a terminator, target_is_dir is false, source is not a directory, and neither --no-target-directory (-T) nor update=older is in effect.

Common situations: Typo in the destination directory name (`mv a.txt outpt/` when the dir is `output`); expecting a directory to exist that a previous mkdir/create step skipped; pointing at a regular file with a trailing slash; CI scripts assuming a build-output directory exists.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/5e136d097c8128bd. Report an issue: GitHub.