sxyazi/yazi · error · io::Error

trash item has no put-back location

Error message

trash item has no put-back location

What it means

Thrown by the Windows trash backend when expanding (listing children of) a trashed directory whose TrashEntry carries no `original` put-back path. Child entries are built as `original.join(name)`, so without a recorded original location the expansion cannot proceed. The Recycle Bin normally records the original location, but items from some network/removable drives or entries reconstructed from a stale TrashId may lack it.

Source

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

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> {
		let top = ShellItem::top(id.top())?;
		let original = top.original()?;
		if !id.has_rel() {
			return top.entry(id.clone(), Some(original));

View on GitHub (pinned to 94abcfa92f)

Solutions

  1. Check `entry.original.is_some()` before calling `list(Some(&entry))` and treat the entry as an opaque leaf when it is None
  2. If the entry should be expandable, re-list the bin from the top (`list(None)`) to rebuild entries with fresh original locations
  3. If put-back info is genuinely unrecorded by the shell, only permanent removal is possible — do not retry listing

Example fix

// before
let children = trash.list(Some(&entry))?;

// after
if entry.original.is_none() {
    // Shell recorded no put-back location; cannot expand children
    return Ok(vec![]);
}
let children = trash.list(Some(&entry))?;
Defensive patterns

Strategy: validation

Validate before calling

// Rust
let expandable = entry.lcha.is_dir() && !entry.lcha.is_indirect() && entry.original.is_some();
if expandable {
    let children = trash.list(Some(&entry))?;
}

Type guard

fn can_expand_trash(e: &TrashEntry) -> bool {
    e.lcha.is_dir() && !e.lcha.is_indirect() && e.original.is_some()
}

Try / catch

match trash.list(Some(&entry)) {
    Err(e) if e.kind() == io::ErrorKind::NotFound && e.to_string().contains("put-back") => Ok(vec![]),
    other => other,
}

Prevention

When it happens

Trigger: Calling `Trash::list(Some(&entry))` on Windows where `entry.lcha` is a directory and not a symlink, but `entry.original` is None — e.g. a trashed directory deleted from a network share, or an entry revived from a cached TrashId without put-back info.

Common situations: Browsing the trash of a machine with items deleted from UNC paths or removable media; restoring a yazi session whose trash state went stale after the Recycle Bin changed; automated tests that construct TrashEntry values without `original`.

Related errors


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