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
- Read the message text itself - it is the complete diagnostic; there is no hidden cause or error kind
- Match the failure in code with `MvFailure::Message(ref s)` and inspect the string, since it carries no io::ErrorKind
- 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
- Do not pattern-match these errors by exact text; the variant is free-form
- When embedding mv, prefer capturing stderr and matching io::ErrorKind-carrying variants (Move/Io) instead of Message
- If you control the code, replace ad-hoc Message(...) errors with typed variants for machine handling
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
- Too many levels of symbolic links
- {}: {error}
- directory stack is empty
- transparent (brush_core::Error)
- failed to create a unique fc temporary file
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/acb29593f7b95c4e.
Report an issue: GitHub.