rust-lang/rust · error
Failed to remove {path}: {err}
Error message
Failed to remove {path}: {err} What it means
This `panic!` in the `rustc_codegen_cranelift` build system fires when `fs::remove_dir_all(download_dir)` — invoked at the start of `clone_repo` to wipe a stale checkout before `git clone` — returns an error other than `NotFound`. `NotFound` is explicitly tolerated (the dir simply doesn't exist yet), but any other I/O failure (permission denied, busy, read-only filesystem, stale NFS handle) aborts the build with the formatted path and OS error.
Source
Thrown at compiler/rustc_codegen_cranelift/build_system/prepare.rs:179
pub(crate) fn patch(&self, dirs: &Dirs) {
self.verify_checksum(dirs);
apply_patches(
dirs,
self.patch_name,
&self.download_dir(dirs),
&self.source_dir().to_path(dirs),
);
}
}
fn clone_repo(download_dir: &Path, repo: &str, rev: &str, submodules: &[&str]) {
eprintln!("[CLONE] {}", repo);
match fs::remove_dir_all(download_dir) {
Ok(()) => {}
Err(err) if err.kind() == io::ErrorKind::NotFound => {}
Err(err) => panic!("Failed to remove {path}: {err}", path = download_dir.display()),
}
// Ignore exit code as the repo may already have been checked out
git_command(None, "clone").arg(repo).arg(download_dir).spawn().unwrap().wait().unwrap();
let mut clean_cmd = git_command(download_dir, "checkout");
clean_cmd.arg("--").arg(".");
spawn_and_wait(clean_cmd);
let mut checkout_cmd = git_command(download_dir, "checkout");
checkout_cmd.arg("-q").arg(rev);
spawn_and_wait(checkout_cmd);
if !submodules.is_empty() {
let mut submodule_cmd = git_command(download_dir, "submodule");
submodule_cmd.arg("update").arg("--init");
submodule_cmd.args(submodules);
spawn_and_wait(submodule_cmd);View on GitHub (pinned to 22057b88b0)
Solutions
- Check the formatted `err` in the panic message — `PermissionDenied` ⇒ fix ownership with `sudo chown -R $USER:$USER <download_dir>`; `Busy`/stale handle ⇒ close the IDE/terminate processes holding the dir.
- Remove the directory manually: `rm -rf <download_dir>` (the path is printed in the panic), then re-run `./y.sh prepare`.
- Confirm the build dir is on a writable filesystem (not a read-only container mount) — relocate the download dir if needed.
- On Windows, disable real-time antivirus for the build tree or move off a OneDrive-synced path.
- Re-run with a clean target: `./y.sh clean && ./y.sh prepare`.
Example fix
# before — running prepare with permission-denied stale checkout ./y.sh prepare # panics: Failed to remove /path/download: Permission denied (os error 13) # after — fix ownership then re-run sudo chown -R $USER:$USER /path/download rm -rf /path/download ./y.sh prepare
Defensive patterns
Strategy: retry
Validate before calling
// prepare.rs remove failure — usually transient (file busy/locked) or a
// permissions issue. Pre-flight check before invoking the build step:
use std::path::Path;
fn removable(p: &Path) -> std::io::Result<()> {
let md = std::fs::metadata(p)?;
if md.permissions().readonly() {
return Err(std::io::Error::new(std::io::ErrorKind::PermissionDenied, "readonly"));
}
Ok(())
}
// Call removable(&path) before `./y.sh prepare`; fix perms or unlock, then retry. Try / catch
// The build step runs as a child process and panics on IO failure,
// so 'catch' = inspect the child exit status and retry with backoff.
use std::{process::Command, thread, time::Duration};
fn run_prepare_retry(n: u32) -> bool {
for attempt in 0..n {
let st = Command::new("./y.sh").arg("prepare").status();
match st {
Ok(s) if s.success() => return true,
_ if attempt + 1 < n => { thread::sleep(Duration::from_secs(2 << attempt)); }
_ => return false,
}
}
false
} Prevention
- Ensure the build user has write/delete permission on the target and out dirs
- Close editors/IDEs/antivirus that may lock artifacts before re-running prepare
- Run the build on a local (non-network) filesystem to avoid flaky removal
- Retry transient removal failures; cranelift build artifacts are safe to delete and regenerate
When it happens
Trigger: Triggered in `clone_repo` (prepare.rs:173) when the build tries to refresh a downloaded upstream repo (e.g. the cranelift-bound test crates) and the pre-clone `fs::remove_dir_all` at line 176 fails with `PermissionDenied`, `Busy`, a stale file handle, or any non-NotFound error. The `match` arm at line 179 panics.
Common situations: Developers running `./y.sh prepare`/`./y.sh build` (the cranelift backend's build script) hit this when: a previous build was killed leaving files owned by root (common after `sudo`), the build runs inside a container/sandbox with a read-only mount of the download dir, an IDE or antivirus holds file handles on Windows, or a network filesystem returns stale handles. Also common in CI when a cache restore produced unreadable files.
Related errors
- Failed to read contents of {path}: {err}
- Failed to remove {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/0c136225191290fa.json.
Report an issue: GitHub.