{"record":{"id":"8e74d7b09259f5e9","repo":"sxyazi/yazi","slug":"trash-item-is-not-a-directory","errorCode":null,"errorMessage":"trash item is not a directory","messagePattern":"trash item is not a directory","errorType":"validation","errorClass":"io::Error","httpStatus":null,"severity":"error","filePath":"yazi-fs/src/trash/freedesktop/trash.rs","lineNumber":22,"sourceCode":"use yazi_macro::ok_or_not_found;\nuse yazi_shim::Twox128;\n\nuse super::{super::{TrashCha, TrashEntries, TrashEntry, TrashId, restore_item}, TrashInfo};\nuse crate::{cha::{Cha, ChaSig}, file::File};\n\npub struct Trash;\n\nimpl Trash {\n\tpub(crate) fn new() -> io::Result<Self> { Ok(Self) }\n\n\tpub(crate) fn list(&self, entry: Option<&TrashEntry>) -> io::Result<Vec<TrashEntry>> {\n\t\tlet Some(entry) = entry else {\n\t\t\treturn self.tops();\n\t\t};\n\n\t\t// TODO\n\t\tif !entry.lcha.is_dir() {\n\t\t\treturn Err(io::Error::new(io::ErrorKind::InvalidInput, \"trash item is not a directory\"));\n\t\t}\n\n\t\tfs::read_dir(&entry.backing)?\n\t\t\t.map(|dent| {\n\t\t\t\tlet dent = dent?;\n\t\t\t\tentry.child(dent.file_name())\n\t\t\t})\n\t\t\t.collect()\n\t}\n\n\tpub(crate) fn entry(&self, id: &TrashId) -> io::Result<TrashEntry> {\n\t\tlet info = TrashInfo::parse(id.top())?;\n\t\tif !os_limited::trash_folders()\n\t\t\t.map_err(io::Error::other)?\n\t\t\t.iter()\n\t\t\t.any(|folder| folder == &info.root)\n\t\t{\n\t\t\treturn Err(io::Error::new(io::ErrorKind::NotFound, \"trash item outside of trash folders\"));","sourceCodeStart":4,"sourceCodeEnd":40,"githubUrl":"https://github.com/sxyazi/yazi/blob/94abcfa92f4ad3f0a1aef6c1ea083cfb8aa6c8c2/yazi-fs/src/trash/freedesktop/trash.rs#L4-L40","documentation":"`Trash::list(Some(entry))` (yazi-fs/src/trash/freedesktop/trash.rs:22) lists the children of a trashed directory by reading `entry.backing`. Only directory entries can be listed; passing a trashed regular file yields `ErrorKind::InvalidInput`, `trash item is not a directory`. The code carries a `// TODO`, acknowledging directory-descent into trash items is incomplete.","triggerScenarios":"Navigating into a trash item in the trash browser: pressing enter/`read_dir` on an entry whose `lcha` says it is a file, or calling `list()` with a file entry instead of `None` (tops).","commonSituations":"Selecting a trashed file (not folder) and issuing an open/enter action that maps to listing; scripts that call list on every entry uniformly.","solutions":["Check `entry.lcha.is_dir()` before calling `list(Some(&entry))`; for files use `entry`/`metadata` or restore/remove instead.","In UI code, make file-type trash entries non-navigable.","Call `list(None)` to get top-level trash items only."],"exampleFix":"// before\nlet children = trash.list(Some(&entry))?; // Err if entry is a file\n\n// after\nif entry.lcha.is_dir() {\n    let children = trash.list(Some(&entry))?;\n} else {\n    // open/restore/remove the file instead of listing it\n}","handlingStrategy":"type-guard","validationCode":"if entry.lcha.is_dir() {\n    let children = trash.list(Some(&entry))?;\n} else {\n    // a file: open/restore it, or show its metadata only\n}","typeGuard":"fn is_listable_trash_entry(entry: &TrashEntry) -> bool {\n    entry.lcha.is_dir()\n}","tryCatchPattern":"match trash.list(Some(&entry)) {\n    Ok(children) => { /* ... */ }\n    Err(e) if e.kind() == io::ErrorKind::InvalidInput => { /* entry is a file: not navigable */ }\n    Err(e) => return Err(e),\n}","preventionTips":["Gate 'enter/navigate' actions in the trash browser on `lcha.is_dir()`.","Use `list(None)` for the top level and only descend into entries you verified are directories.","Remember directory-descent inside trash is marked TODO upstream; keep expectations minimal."],"tags":["trash","freedesktop","list","not-a-directory"],"backgroundTag":null,"analyzedSha":"94abcfa92f4ad3f0a1aef6c1ea083cfb8aa6c8c2","analyzedAt":"2026-08-16T09:56:24.836Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}