{"record":{"id":"be6ef04c12e5657b","repo":"sxyazi/yazi","slug":"trash-item-is-not-a-directory-be6ef0","errorCode":null,"errorMessage":"trash item is not a directory","messagePattern":"trash item is not a directory","errorType":"exception","errorClass":"io::Error","httpStatus":null,"severity":"error","filePath":"yazi-fs/src/trash/windows/trash.rs","lineNumber":28,"sourceCode":"\tstatic COM: io::Result<Com> = Com::new();\n}\n\npub struct Trash;\n\nimpl Trash {\n\tpub(crate) fn new() -> io::Result<Self> {\n\t\tCOM.with(|result| {\n\t\t\tresult.as_ref().map(|_| Self).map_err(|e| io::Error::new(e.kind(), e.to_string()))\n\t\t})\n\t}\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\tif !entry.lcha.is_dir() || entry.lcha.is_indirect() {\n\t\t\treturn Err(io::Error::new(io::ErrorKind::InvalidInput, \"trash item is not a directory\"));\n\t\t}\n\n\t\tlet original = entry.original.as_deref().ok_or_else(|| {\n\t\t\tio::Error::new(io::ErrorKind::NotFound, \"trash item has no put-back location\")\n\t\t})?;\n\n\t\tself\n\t\t\t.resolve(entry)?\n\t\t\t.children()?\n\t\t\t.into_iter()\n\t\t\t.map(|item| {\n\t\t\t\tlet name = item.display_name(SIGDN_PARENTRELATIVEPARSING)?;\n\t\t\t\titem.entry(entry.id.child(&name)?, Some(original.join(&name)))\n\t\t\t})\n\t\t\t.collect()\n\t}\n\n\tpub(crate) fn entry(&self, id: &TrashId) -> io::Result<TrashEntry> {","sourceCodeStart":10,"sourceCodeEnd":46,"githubUrl":"https://github.com/sxyazi/yazi/blob/94abcfa92f4ad3f0a1aef6c1ea083cfb8aa6c8c2/yazi-fs/src/trash/windows/trash.rs#L10-L46","documentation":"Returned by Trash::list on Windows when it is called with an entry that is not a directory or is 'indirect' (a reparse point — symlink, junction, mount). Children are enumerated through the shell item's original (pre-deletion) path, so expanding requires a real directory with a put-back location; InvalidInput rejects files and reparse points alike.","triggerScenarios":"Trash:list(entry) where entry.lcha.is_dir() is false or entry.lcha.is_indirect() is true (symlinked folder inside the recycle bin); note the sibling NotFound error at line 31 fires first when original is missing.","commonSituations":"Expanding a trashed symlink/junction to a directory in the trash tab; plugins calling list on the hovered entry without kind checks; entries restyled after revalidate races.","solutions":["Before list(entry), require entry.lcha.is_dir() and not entry.lcha.is_indirect().","For symlinked directories, resolve the target outside the trash API instead of expanding it in place.","Re-fetch the entry if metadata may be stale, then re-check."],"exampleFix":"-- Lua: before\nlocal items = ya.fs(\"trash\"):list(entry)\n-- after\nlocal e = ya.fs(\"trash\"):entry(entry) -- refresh\nlocal items = e and e.lcha.is_dir and not e.lcha.is_indirect and ya.fs(\"trash\"):list(e) or nil","handlingStrategy":"type-guard","validationCode":"-- Lua: expand only real, non-reparse directories\nif entry == nil or (entry.lcha.is_dir and not entry.lcha.is_indirect) then\n  local items = ya.fs(\"trash\"):list(entry)\nend","typeGuard":"local function is_expandable(e)\n  return e ~= nil and e.lcha ~= nil and e.lcha.is_dir == true and e.lcha.is_indirect ~= true\nend","tryCatchPattern":"local items, err = ya.fs(\"trash\"):list(entry)\nif items == nil then\n  if err.kind == \"invalid-input\" then -- file or symlink: not expandable\n  elseif err.kind == \"not-found\" then -- no put-back location: cannot enumerate children\n  end\nend","preventionTips":["Check is_dir AND is_indirect on lcha before expanding — symlinks/junctions are rejected even when they target directories.","Refresh the entry before expanding if the view may be stale.","Handle the sibling 'no put-back location' NotFound error distinctly."],"tags":["trash","windows","invalid-input","directory","reparse-point","listing"],"backgroundTag":null,"analyzedSha":"94abcfa92f4ad3f0a1aef6c1ea083cfb8aa6c8c2","analyzedAt":"2026-08-16T09:56:24.836Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}