can1357/oh-my-pi · info

{0}

Error message

{0}

What it means

MvFailure::Message(String) is the catch-all variant of mv's failure enum. It wraps a free-form diagnostic string verbatim ({0} is the string itself) with no additional context, unlike MvError or io::Error variants which add their own prefixes. When you see a bare message with no `mv:`-style formatting, it came from code that formatted its own complete error text and wrapped it via MvFailure::Message (or an equivalent .into()).

Source

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

	#[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,
}

matches_parser!(Mv, app);

/// A terminal-like indicatif sink backed by the command's stderr.
struct ProgressTerminal {
	writer: Mutex<OpenFile>,
}

impl fmt::Debug for ProgressTerminal {

View on GitHub (pinned to 9690622007)

Solutions

  1. Read the message text itself - it is the complete diagnostic; there is no hidden cause or error kind
  2. Match the failure in code with `MvFailure::Message(ref s)` and inspect the string, since it carries no io::ErrorKind
  3. If you maintain this code, prefer a structured MvError or io::Error::new(kind, msg) variant so callers can match on error kinds instead of strings

Example fix

// before
return Err(format!("custom failure for {path}").into()); // -> MvFailure::Message
// after
return Err(io::Error::new(io::ErrorKind::InvalidInput, format!("custom failure for {path}")).into());
Defensive patterns

Strategy: try-catch

Type guard

fn is_message_failure(f: &MvFailure) -> Option<&str> {
  match f { MvFailure::Message(s) => Some(s.as_str()), _ => None }
}

Try / catch

match mv_result {
  Err(MvFailure::Message(msg)) => {
    log::warn!("mv failed with unstructured message: {msg}");
    // handle by string content; no ErrorKind available
  }
  Err(other) => return Err(other),
  Ok(_) => {}
}

Prevention

When it happens

Trigger: Any code path in mv that constructs a custom error string and converts it to MvFailure::Message instead of a structured MvError or io::Error; since the variant is transparent ({0}), the output is exactly that string with no source path or error-kind decoration.

Common situations: Debugging mv failures whose messages lack the usual `cannot stat`/`target` prefixes, making them hard to grep for; downstream tooling that pattern-matches mv's stderr will not recognize these bare messages; contributor code that used format!(...).into() for an error.

Related errors


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