sxyazi/yazi · error · io::Error
item is not in the trash
Error message
item is not in the trash
What it means
Returned by Trash::entry on macOS when the TrashId's top path is not a direct child of the trash root (~/.Trash). The macOS backend models trash as top-level items under ~/.Trash plus a relative path inside them; an id whose top lives anywhere else is rejected with io::ErrorKind::InvalidInput.
Source
Thrown at yazi-fs/src/trash/macos/trash.rs:50
it.map(|dent| {
let dent = dent?;
if let Some(entry) = entry {
entry.child(dent.file_name())
} else {
let path = dent.path();
let original = store
.get(path.file_name().unwrap_or_default())
.and_then(|ds| ds.join(Path::new("")).ok());
TrashEntry::top(path.clone(), path, original)
}
})
.collect()
}
pub(crate) fn entry(&self, id: &TrashId) -> io::Result<TrashEntry> {
let root = self.root()?;
if id.top().parent() != Some(&root) {
return Err(io::Error::new(io::ErrorKind::InvalidInput, "item is not in the trash"));
}
if id.has_rel() && !fs::symlink_metadata(id.top())?.file_type().is_dir() {
return Err(io::Error::new(io::ErrorKind::InvalidInput, "trash item is not a directory"));
}
let store = DsStore::parse(&root.join(".DS_Store")).unwrap_or_default();
let name = id.top().file_name().unwrap_or_default();
let original = store.get(name).and_then(|ds| ds.join(id.rel()).ok());
TrashEntry::new(id.clone(), id.path(), original)
}
pub(crate) fn metadata(&self, entry: &TrashEntry, follow: bool) -> io::Result<Cha> {
Ok(if follow { entry.cha } else { entry.lcha })
}
pub(crate) fn revalidate(
&self,View on GitHub (pinned to 94abcfa92f)
Solutions
- Always obtain ids from Trash:list/Trash:entry results instead of constructing them manually.
- If building by hand, set top to an absolute path directly inside ~/.Trash and put deeper segments in rel.
- Verify HOME is stable (dirs::home_dir must resolve to the same absolute root) across the call.
Example fix
-- Lua: before (nested path as top)
local e = ya.fs("trash"):entry({ top = "/Users/me/.Trash/Project/a.txt", rel = "" })
-- after (top is direct child of ~/.Trash, rest is rel)
local e = ya.fs("trash"):entry({ top = "/Users/me/.Trash/Project", rel = "a.txt" }) Defensive patterns
Strategy: validation
Validate before calling
-- Lua: verify top is a direct child of ~/.Trash before lookup
local home = ya.claim_to_be_home -- or obtain HOME from ya
local root = "/Users/" .. ya.user_name .. "/.Trash" -- better: reuse top from a listed entry
local function top_in_trash(top)
return top:sub(1, #root + 1) == root .. "/" and not top:find("/", #root + 2)
end Type guard
local function is_valid_trash_id(t)
return type(t) == "table"
and type(t.top) == "string" and #t.top > 0
and type(t.rel) == "string" and t.rel:sub(1, 1) ~= "/"
end Try / catch
local e, err = ya.fs("trash"):entry(id)
if e == nil and err.kind == "invalid-input" then
-- id was hand-built: re-list and match by name instead of retrying
end Prevention
- Always source ids from list()/entry() results; never synthesize them from Urls.
- Keep top = direct child of ~/.Trash and move deeper segments into rel.
- Ensure HOME is unchanged between listing and lookup.
When it happens
Trigger: Calling Trash:entry with a hand-built Lua table { top = '/Users/me/some/path', rel = '' } where top.parent() != ~/.Trash; using a filesystem path instead of an id obtained from Trash:list; HOME resolution changed between listing and lookup so the roots disagree.
Common situations: Plugins constructing ids from Urls or strings instead of reusing entries from list(); ids persisted across sessions after HOME moved; passing a nested path (e.g. ~/.Trash/dir/file) as top instead of top=~/.Trash/dir with rel=file.
Related errors
- invalid trash entry path
- trash item is not a directory
- invalid trash entry
- invalid trash entry path
- invalid trash item path
AI-assisted analysis of sxyazi/yazi@94abcfa92f (2026-08-16).
Data as JSON: /api/errors/0c7229347c25f0da.
Report an issue: GitHub.