sxyazi/yazi · error · io::Error

InvalidInput

InvalidInput

Error message

trash item is not a directory

What it means

In the Windows trash backend, `list()` with a parent `TrashEntry` enumerates its children in the recycle-bin folder. This error is returned when the parent entry's cached attributes (`lcha`) indicate it is not a directory, or is an indirect/link item, so it cannot have children to enumerate. It is a caller-contract violation: `list(Some(entry))` must be given a directory trash item.

Source

Thrown at yazi-fs/src/trash/windows/trash.rs:28

	static COM: io::Result<Com> = Com::new();
}

pub struct Trash;

impl Trash {
	pub(crate) fn new() -> io::Result<Self> {
		COM.with(|result| {
			result.as_ref().map(|_| Self).map_err(|e| io::Error::new(e.kind(), e.to_string()))
		})
	}

	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() || entry.lcha.is_indirect() {
			return Err(io::Error::new(io::ErrorKind::InvalidInput, "trash item is not a directory"));
		}

		let original = entry.original.as_deref().ok_or_else(|| {
			io::Error::new(io::ErrorKind::NotFound, "trash item has no put-back location")
		})?;

		self
			.resolve(entry)?
			.children()?
			.into_iter()
			.map(|item| {
				let name = item.display_name(SIGDN_PARENTRELATIVEPARSING)?;
				item.entry(entry.id.child(&name)?, Some(original.join(&name)))
			})
			.collect()
	}

	pub(crate) fn entry(&self, id: &TrashId) -> io::Result<TrashEntry> {

View on GitHub (pinned to 5f901b886b)

Solutions

  1. Check `entry.lcha.is_dir() && !entry.lcha.is_indirect()` before calling list with a parent entry.
  2. Only pass directory entries to the child enumeration path; list files as leaves.
  3. Re-fetch the entry's metadata if the cached lcha may be stale.
  4. Treat the error as InvalidInput and skip non-directory entries during traversal.

Example fix

// before
let children = trash.list(Some(&entry))?;
// after
if entry.lcha.is_dir() && !entry.lcha.is_indirect() {
    let children = trash.list(Some(&entry))?;
} else {
    // leaf entry: no children
}
Defensive patterns

Strategy: validation

Validate before calling

if !(entry.lcha.is_dir() && !entry.lcha.is_indirect()) {
    return Ok(()); // leaf: no children to list
}
let children = trash.list(Some(&entry))?;

Type guard

fn is_listable_dir(entry: &TrashEntry) -> bool {
    entry.lcha.is_dir() && !entry.lcha.is_indirect()
}

Try / catch

match trash.list(Some(&entry)) {
    Ok(children) => children,
    Err(e) if e.kind() == std::io::ErrorKind::InvalidInput => Vec::new(),
    Err(e) => return Err(e.into()),
}

Prevention

When it happens

Trigger: Calling `Trash::list(Some(entry))` where `entry.lcha` is not a dir or `is_indirect()` is true — e.g. passing a trashed file instead of a trashed folder, or a shortcut/indirect item.

Common situations: Building a tree view of the recycle bin and feeding non-directory entries into the child-listing call; stale attribute caches after the entry changed.

Related errors


AI-assisted analysis of sxyazi/yazi@5f901b886b (2026-09-02). Data as JSON: /api/errors/839c93c685257457. Report an issue: GitHub.