sxyazi/yazi · error · io::Error

invalid trash info path

Error message

invalid trash info path

What it means

`TrashInfo::parse` (freedesktop/trash_info.rs:19) requires its input path to end with the literal extension `.trashinfo`; anything else is rejected with `ErrorKind::InvalidData`, `invalid trash info path`. The extension is what maps an id to its metadata file under `<trash-root>/info/`.

Source

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

use std::{borrow::Cow, ffi::OsStr, fs::File, io::{self, BufRead, BufReader}, os::unix::ffi::OsStrExt, path::{Path, PathBuf}};

use percent_encoding::percent_decode;
use uzers::Users;
use yazi_shared::USERS_CACHE;
use yazi_shim::path::PathExt;

pub(super) struct TrashInfo {
	pub(super) root:     PathBuf,
	pub(super) backing:  PathBuf,
	pub(super) original: PathBuf,
}

impl TrashInfo {
	// Parses from a trashinfo path, e.g.:
	//   /home/alice/.local/share/Trash/info/cat.jpg.trashinfo
	pub(super) fn parse(info: &Path) -> io::Result<Self> {
		if info.extension() != Some(OsStr::new("trashinfo")) {
			return Err(io::Error::new(io::ErrorKind::InvalidData, "invalid trash info path"));
		}

		// /home/alice/.local/share/Trash
		let root = info
			.parent()
			.filter(|p| p.file_name() == Some(OsStr::new("info")))
			.and_then(Path::parent)
			.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "invalid trash info path"))?;

		// cat.jpg
		let stem = info
			.file_stem()
			.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "invalid trash info path"))?;

		let original = Self::parse_original(info, root)?;
		if original.file_name().is_none() {
			return Err(io::Error::new(io::ErrorKind::InvalidData, "invalid original trash path"));
		}

View on GitHub (pinned to 94abcfa92f)

Solutions

  1. Only use ids returned by `Trash::list`/`tops`; their tops always carry the `.trashinfo` extension.
  2. If constructing ids, build them as `<trash_root>/info/<stem>.trashinfo`.
  3. Validate `path.extension() == Some("trashinfo")` before passing an id top to `entry()`.

Example fix

// before
let entry = trash.entry(&id)?; // id.top() == ".../files/cat.jpg" -> InvalidData

// after
let top = id.top();
if top.extension() != Some(OsStr::new("trashinfo")) { /* rebuild id from list() */ }
let entry = trash.entry(&id)?;
Defensive patterns

Strategy: validation

Validate before calling

use std::ffi::OsStr;

if id.top().extension() != Some(OsStr::new("trashinfo")) {
    // rebuild the id from trash.list() instead of proceeding
}

Type guard

fn is_trashinfo_path(p: &std::path::Path) -> bool {
    p.extension() == Some(std::ffi::OsStr::new("trashinfo"))
}

Try / catch

match trash.entry(&id) {
    Ok(e) => { /* ... */ }
    Err(e) if e.kind() == io::ErrorKind::InvalidData => { /* id top is not a .trashinfo path */ }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling `Trash::entry(&id)` where `id.top()` is a path without the `.trashinfo` extension — a path into `files/`, the trash root itself, or an arbitrary file.

Common situations: Hand-built `TrashId`s; string manipulation that strips or mangles the extension; passing the backing (files/) path where the info path was intended.

Related errors


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