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
- Check the file exists immediately before trashing; if the target is gone, treat as already-deleted success
- On headless Linux, set XDG_DATA_HOME and ensure the trash directories exist, or offer permanent delete as the fallback path
- Verify the daemon user can move the file into the trash directory (permissions on file, parent, and trash dir)
- 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
- On headless Linux, ensure XDG_DATA_HOME and trash dirs exist, or default to permanent delete
- Always offer a permanent-delete fallback for volumes without trash support
- Check existence right before deletion to avoid races with sync software
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
- Path does not exist: {}
- Path must be a directory: {}
- Could not determine binary directory
- Failed to start daemon: {}
- Could not determine home directory
AI-assisted analysis of spacedriveapp/spacedrive@6dfeccf211 (2026-08-16).
Data as JSON: /api/errors/44a2aeee7aa5aa8e.
Report an issue: GitHub.