can1357/oh-my-pi · error

target directory {0}: Not a directory

Error message

target directory {0}: Not a directory

What it means

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.

Source

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

};

#[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.

View on GitHub (pinned to 9690622007)

Solutions

  1. Create the directory before invoking: `mkdir -p "$(target)"` then `mv -t "$target" sources...`.
  2. Double-check the value passed to -t; it must be an existing directory, not a destination filename.
  3. Verify mounts/variables that supply the path (`echo $target; ls -ld "$target"`).
  4. If you meant a single rename, drop `-t` and use `mv src dest`.

Example fix

// before (fails: $out is not an existing directory)
mv -t "$out" *.log

// after
mkdir -p "$out" && mv -t "$out" *.log
Defensive patterns

Strategy: validation

Validate before calling

import * as fs from "node:fs/promises";

export async function resolveTargetDir(dir: string): Promise<string> {
  const st = await fs.stat(dir).catch(() => null);
  if (!st) await fs.mkdir(dir, { recursive: true });
  else if (!st.isDirectory()) throw new Error(`-t target ${dir} exists but is not a directory`);
  return dir;
}
// const t = await resolveTargetDir(target); mv -t t sources...

Try / catch

try {
  await runBuiltin("mv", ["-t", target, ...sources]);
} catch (e) {
  if (String(e).startsWith("target directory ") && String(e).includes("Not a directory")) {
    logger.error("--target-directory must be an existing directory", { target });
  } else throw e;
}

Prevention

When it happens

Trigger: `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.

Common situations: 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.

Related errors


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