rust-lang/rust · error
Failed to remove {path}: {err}
Error message
Failed to remove {path}: {err} What it means
This `panic!` fires inside `ensure_empty_dir` (utils.rs:194-198) while iterating directory entries, when removing a sub-directory via `fs::remove_dir_all(entry.path())` returns an error other than `NotFound`. `NotFound` is tolerated (a concurrent remover already deleted it), but any other I/O failure (permission denied, dir not empty due to concurrent writes, busy handle) halts the cranelift build-system. The entry path and OS error are formatted into the message.
Source
Thrown at compiler/rustc_codegen_cranelift/build_system/utils.rs:197
/// Create the specified directory if it doesn't exist yet and delete all contents.
pub(crate) fn ensure_empty_dir(path: &Path) {
fs::create_dir_all(path).unwrap();
let read_dir = match fs::read_dir(path) {
Ok(read_dir) => read_dir,
Err(err) if err.kind() == io::ErrorKind::NotFound => {
return;
}
Err(err) => {
panic!("Failed to read contents of {path}: {err}", path = path.display())
}
};
for entry in read_dir {
let entry = entry.unwrap();
if entry.file_type().unwrap().is_dir() {
match fs::remove_dir_all(entry.path()) {
Ok(()) => {}
Err(err) if err.kind() == io::ErrorKind::NotFound => {}
Err(err) => panic!("Failed to remove {path}: {err}", path = entry.path().display()),
}
} else {
match fs::remove_file(entry.path()) {
Ok(()) => {}
Err(err) if err.kind() == io::ErrorKind::NotFound => {}
Err(err) => panic!("Failed to remove {path}: {err}", path = entry.path().display()),
}
}
}
}
pub(crate) fn copy_dir_recursively(from: &Path, to: &Path) {
for entry in fs::read_dir(from).unwrap() {
let entry = entry.unwrap();
let filename = entry.file_name();
if filename == "." || filename == ".." {
continue;
}View on GitHub (pinned to 22057b88b0)
Solutions
- Inspect the `err` and `path` in the panic message: `PermissionDenied` ⇒ fix ownership (`sudo chown -R $USER:$USER <entry path>`).
- Ensure no other process (cargo, IDE, file indexer) is touching the build tree; on Windows close VS Code / disable Defender for the path.
- Manually remove the offending sub-directory: `rm -rf <entry path>`, then re-run `./y.sh prepare`.
- Re-run `ensure_empty_dir` after a `./y.sh clean` to start from a known-empty state.
- Relocate the build dir off network/FUSE filesystems onto local disk.
Example fix
# before — ensure_empty_dir panics removing a sub-dir ./y.sh prepare # panics: Failed to remove /path/dir/sub: Permission denied # after — fix ownership of the sub-tree and re-run sudo chown -R $USER:$USER /path/dir rm -rf /path/dir/sub ./y.sh prepare
Defensive patterns
Strategy: retry
Validate before calling
// utils.rs:197 remove failure — same class as 56: transient/perms/lock.
// Pre-flight: confirm the path is gone-able before the build removes it.
use std::path::Path;
fn ensure_removable(p: &Path) -> std::io::Result<()> {
match std::fs::metadata(p) {
Ok(md) if md.permissions().readonly() =>
Err(std::io::Error::new(std::io::ErrorKind::PermissionDenied, "readonly")),
Ok(_) => Ok(()),
Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(()), // nothing to remove
Err(e) => Err(e),
}
} Try / catch
// Retry remove with backoff; surface final cause if it stays stuck.
use std::{fs, path::Path, thread, time::Duration};
fn remove_retry(p: &Path, n: u32) -> std::io::Result<()> {
let mut last = None;
for a in 0..n {
match fs::remove_file(p) {
Ok(()) => return Ok(()),
Err(e) if e.kind() == std::io::ErrorKind::NotFound => return Ok(()),
Err(e) => { last = Some(e); if a + 1 < n { thread::sleep(Duration::from_millis(100 << a)); } }
}
}
Err(last.unwrap())
} Prevention
- Make sure no other process holds the file open (editors, indexers, AV scans)
- Confirm write/delete permission on the containing directory, not just the file
- Prefer a clean build dir per CI run so stale locks don't accumulate
- Retry with backoff; removal failures are frequently transient
When it happens
Trigger: Triggered when `ensure_empty_dir` walks the directory and `fs::remove_dir_all` at line 194 fails for a directory entry with a non-NotFound error. Reachable when the build tree contains a sub-directory whose contents are held open by another process, owned by another user, or on a read-only mount.
Common situations: Seen in cranelift local/CI builds when: a previous interrupted build left sub-dirs owned by root, an IDE/antivirus/Indexer holds handles on Windows, a process is actively writing into the dir (concurrent cargo), or a NFS/SMB mount returns stale file handles during recursive removal.
Related errors
- Failed to remove {path}: {err}
- Failed to read contents of {path}: {err}
- failed to create {dst:?}: {e}
- failed to copy {src:?}->{dst:?}: {e}
- prologue for {:?}
AI-assisted analysis of rust-lang/rust@22057b88b0 (2026-08-03).
Data as JSON: /data/errors/6498f009e320bc0b.json.
Report an issue: GitHub.