sxyazi/yazi · error · io::Error

trash item is not a directory

Error message

trash item is not a directory

What it means

`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.

Source

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

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();
		};

		// TODO
		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 94abcfa92f)

Solutions

  1. Check `entry.lcha.is_dir()` before calling `list(Some(&entry))`; for files use `entry`/`metadata` or restore/remove instead.
  2. In UI code, make file-type trash entries non-navigable.
  3. Call `list(None)` to get top-level trash items only.

Example fix

// before
let children = trash.list(Some(&entry))?; // Err if entry is a file

// after
if entry.lcha.is_dir() {
    let children = trash.list(Some(&entry))?;
} else {
    // open/restore/remove the file instead of listing it
}
Defensive patterns

Strategy: type-guard

Validate before calling

if entry.lcha.is_dir() {
    let children = trash.list(Some(&entry))?;
} else {
    // a file: open/restore it, or show its metadata only
}

Type guard

fn is_listable_trash_entry(entry: &TrashEntry) -> bool {
    entry.lcha.is_dir()
}

Try / catch

match trash.list(Some(&entry)) {
    Ok(children) => { /* ... */ }
    Err(e) if e.kind() == io::ErrorKind::InvalidInput => { /* entry is a file: not navigable */ }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: 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).

Common situations: Selecting a trashed file (not folder) and issuing an open/enter action that maps to listing; scripts that call list on every entry uniformly.

Related errors


AI-assisted analysis of sxyazi/yazi@94abcfa92f (2026-08-16). Data as JSON: /api/errors/8e74d7b09259f5e9. Report an issue: GitHub.