spacedriveapp/spacedrive · error · std::io::Error

Failed to move to trash: {}

Error message

Failed to move to trash: {}

What it means

move_to_trash runs trash::delete on a blocking thread and wraps any error into io::Error with ErrorKind::Other; a JoinError from spawn_blocking is wrapped the same way. The trash crate uses NSFileManager on macOS, SHFileOperation on Windows, and the XDG trash spec on Linux, so the concrete failure causes are platform-specific.

Source

Thrown at core/src/ops/files/delete/strategy.rs:227

				}
			}
		}

		Ok(total)
	}

	/// Move file to the system trash/recycle bin.
	///
	/// Uses the `trash` crate for native platform support:
	/// - Windows: SHFileOperation → Recycle Bin
	/// - macOS: NSFileManager → Trash
	/// - Linux: XDG trash spec
	#[cfg(any(target_os = "windows", target_os = "macos", target_os = "linux"))]
	pub async fn move_to_trash(&self, path: &Path) -> Result<(), std::io::Error> {
		let path = path.to_path_buf();
		tokio::task::spawn_blocking(move || {
			trash::delete(&path).map_err(|e| {
				std::io::Error::new(
					std::io::ErrorKind::Other,
					format!("Failed to move to trash: {}", e),
				)
			})
		})
		.await
		.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))??;

		Ok(())
	}

	#[cfg(not(any(target_os = "windows", target_os = "macos", target_os = "linux")))]
	pub async fn move_to_trash(&self, _path: &Path) -> Result<(), std::io::Error> {
		Err(std::io::Error::new(
			std::io::ErrorKind::Unsupported,
			"move to trash is not supported on this platform",
		))
	}

View on GitHub (pinned to 6dfeccf211)

Solutions

  1. Check the file exists immediately before trashing; if the target is gone, treat as already-deleted success
  2. On headless Linux, set XDG_DATA_HOME and ensure the trash directories exist, or offer permanent delete as the fallback path
  3. Verify the daemon user can move the file into the trash directory (permissions on file, parent, and trash dir)
  4. Confirm the platform is windows/macos/linux: the cfg gate compiles this helper out elsewhere, so a wrong-platform build fails at link/compile instead

Example fix

// before: trash failure aborts the delete flow
strategy.move_to_trash(&path).await?;

// after: fall back to permanent delete when trash is unavailable (e.g. headless Linux)
if strategy.move_to_trash(&path).await.is_err() {
    tracing::warn!(?path, "Trash failed; falling back to permanent delete");
    strategy.permanent_delete(&path).await?;
}
Defensive patterns

Strategy: fallback

Validate before calling

// Before trashing, confirm the path still exists
match tokio::fs::symlink_metadata(path).await {
    Ok(_) => { /* proceed */ }
    Err(e) if e.kind() == std::io::ErrorKind::NotFound => return Ok(()), // already gone
    Err(e) => return Err(e),
}

Try / catch

match strategy.move_to_trash(path).await {
    Err(e) => {
        tracing::warn!(?path, error = %e, "trash unavailable; falling back to permanent delete");
        strategy.permanent_delete(path).await // or surface a user choice
    }
    ok => ok,
}

Prevention

When it happens

Trigger: Path no longer exists when trash::delete runs (deleted by another process first); headless Linux without an XDG trash implementation (no trash service or ~/.local/share/Trash infra); file on a mount the trash spec cannot handle (some network mounts, tmpfs); permission denied on the file or parent; path with characters the platform API rejects.

Common situations: Daemon running in a container or headless server where XDG trash directories do not exist; trashing from external or network volumes; racing with sync software that removes the file first.

Related errors


AI-assisted analysis of spacedriveapp/spacedrive@6dfeccf211 (2026-08-16). Data as JSON: /api/errors/44a2aeee7aa5aa8e. Report an issue: GitHub.