sxyazi/yazi · error

trash item is not a directory

Error message

trash item is not a directory

What it means

The freedesktop Trash::list lists the children of a trashed directory entry. Because listing children of a regular file is meaningless (and marked TODO in the source), it rejects non-directory entries up-front with InvalidInput "trash item is not a directory". Only trashed directories can be expanded into child entries.

Solutions

  1. Check entry.lcha.is_dir() (or the entry's kind) before calling list(); skip files or emit them as leaves.
  2. Use the flat trash listing (tops()) and handle files and directories differently in the caller.
  3. Catch io::ErrorKind::InvalidInput from list() and treat it as an empty children set for file entries.

Example fix

// before
let children = trash.list(&entry).await?;
// after
let children = if entry.lcha.is_dir() {
    trash.list(&entry).await?
} else {
    vec![] // files have no children
};
Defensive patterns

Strategy: type-guard

Validate before calling

// Rust
if !entry.lcha.is_dir() { return Ok(vec![]); }

Type guard

// Rust
fn is_trashed_dir(entry: &TrashEntry) -> bool { entry.lcha.is_dir() }

Try / catch

let children = match trash.list(&entry).await {
    Err(e) if e.kind() == std::io::ErrorKind::InvalidInput => Vec::new(), // file entry
    other => other?,
};

Prevention

When it happens

Trigger: Calling list() with a trash entry that points to a trashed file rather than a trashed directory — e.g. asking the trash provider to enumerate children of "cat.jpg".

Common situations: A UI or plugin navigates into a trashed file as if it were a folder; stale UI state shows a deleted file as expandable; code iterates all trash entries and calls list() on each without filtering directories.

Related errors


AI-assisted analysis of sxyazi/yazi@a541adde4e (2026-09-18). Data as JSON: /api/errors/8e74d7b09259f5e9. Report an issue: GitHub.

Appendix: source

Thrown at yazi-fs/src/trash/freedesktop/trash.rs:21

use trash::os_limited;
use yazi_macro::ok_or_not_found;
use yazi_shim::Twox128;

use super::{super::{TrashCha, TrashEntries, TrashEntry, TrashId, restore_item}, TrashInfo};
use crate::{cha::{Cha, ChaSig}, file::File};

pub struct Trash;

impl Trash {
	pub(crate) fn new() -> io::Result<Self> { Ok(Self) }

	pub(crate) fn list(&self, entry: Option<&TrashEntry>) -> io::Result<Vec<TrashEntry>> {
		let Some(entry) = entry else {
			return self.tops();
		};

		if !entry.lcha.is_dir() {
			return Err(io::Error::new(io::ErrorKind::InvalidInput, "trash item is not a directory"));
		}

		fs::read_dir(&entry.backing)?
			.map(|dent| {
				let dent = dent?;
				entry.child(dent.file_name())
			})
			.collect()
	}

	pub(crate) fn entry(&self, id: &TrashId) -> io::Result<TrashEntry> {
		let info = TrashInfo::parse(id.top())?;
		if !os_limited::trash_folders()
			.map_err(io::Error::other)?
			.iter()
			.any(|folder| folder == &info.root)
		{
			return Err(io::Error::new(io::ErrorKind::NotFound, "trash item outside of trash folders"));

View on GitHub (pinned to a541adde4e)