sxyazi/yazi · error · io::Error
invalid trash item path
Error message
invalid trash item path
What it means
`TrashEntry::new` (yazi-fs/src/trash/entry.rs:43) needs a display/stat name for the entry and tries three sources in order: the file name of the id's relative path, the original put-back path's file name, then the backing path's file name. If all three are missing or empty (e.g. paths ending in `..` or empty), it fails with `ErrorKind::InvalidInput`, `invalid trash item path`.
Source
Thrown at yazi-fs/src/trash/entry.rs:43
fn deref(&self) -> &Self::Target { &self.id }
}
impl TrashEntry {
#[cfg(trash_unix)]
pub(super) fn new<B>(id: TrashId, backing: B, original: Option<PathBuf>) -> io::Result<Self>
where
B: Into<PathBuf>,
{
use super::TrashCha;
let backing = backing.into();
let name = id
.rel()
.file_name()
.or_else(|| original.as_deref().and_then(Path::file_name))
.or_else(|| backing.file_name())
.filter(|name| !name.is_empty())
.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "invalid trash item path"))?;
let (lcha, cha) = Cha::from_trash(&backing, name)?;
let link_to = if lcha.is_link() { std::fs::read_link(&backing).ok() } else { None };
Ok(Self { id, cha, lcha, original, link_to, backing })
}
#[cfg(trash_unix)]
pub(super) fn top<T, B>(top: T, backing: B, original: Option<PathBuf>) -> io::Result<Self>
where
T: Into<PathBuf>,
B: Into<PathBuf>,
{
Self::new(TrashId::new(top, PathBuf::new())?, backing, original)
}
#[cfg(trash_unix)]
pub(super) fn child(&self, name: OsString) -> io::Result<Self> {View on GitHub (pinned to 94abcfa92f)
Solutions
- Only build entries from ids produced by the trash implementation itself (`Trash::list`/`entry`), never synthesize ids by hand.
- Validate `id.rel()` is a plain relative path with a non-empty final component before calling.
- Purge the damaged trash item (`rm` the backing file and its `.trashinfo`) so listing stops constructing it.
Example fix
// before
let entry = TrashEntry::new(id, backing, None)?; // rel == ".." -> InvalidInput
// after
let ok = id.rel().file_name().is_some_and(|n| !n.is_empty())
|| backing.file_name().is_some_and(|n| !n.is_empty());
if !ok { /* skip this damaged item */ }
let entry = TrashEntry::new(id, backing, None)?; Defensive patterns
Strategy: validation
Validate before calling
fn has_any_name(id: &TrashId, original: Option<&std::path::Path>, backing: &std::path::Path) -> bool {
let nonempty = |n: Option<&std::ffi::OsStr>| n.is_some_and(|n| !n.is_empty());
nonempty(id.rel().file_name())
|| original.and_then(std::path::Path::file_name).is_some_and(|n| !n.is_empty())
|| nonempty(backing.file_name())
} Type guard
fn is_usable_trash_id(id: &TrashId, backing: &std::path::Path) -> bool {
id.rel().file_name().is_some_and(|n| !n.is_empty())
|| backing.file_name().is_some_and(|n| !n.is_empty())
} Try / catch
match TrashEntry::new(id, backing, original) {
Ok(e) => { /* ... */ }
Err(e) if e.kind() == io::ErrorKind::InvalidInput => { /* drop the damaged id; continue listing */ }
Err(e) => return Err(e),
} Prevention
- Never synthesize TrashIds by string concatenation; take them from Trash::list/entry.
- Reject ids whose rel() ends in `..` before use.
- When listing trash, tolerate per-item InvalidInput and skip damaged entries instead of failing the listing.
When it happens
Trigger: Building a `TrashEntry` from a `TrashId` whose `rel()` ends in `..` or is degenerate, while `original` is None and `backing` has no file name (e.g. `/` or `foo/..`). Also reachable via `TrashEntry::child()` when the child name is `..`-like.
Common situations: Hand-constructed or stale trash ids from plugins/Lua; trash metadata damaged so no original path is known and the backing file was removed.
Related errors
- invalid trash info path
- invalid original trash path
- invalid trash entry path
- trash item is not a directory
- item is not in the trash
AI-assisted analysis of sxyazi/yazi@94abcfa92f (2026-08-16).
Data as JSON: /api/errors/8ffeac7790f67653.
Report an issue: GitHub.