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

  1. 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.
  2. Remove the directory manually: `rm -rf <download_dir>` (the path is printed in the panic), then re-run `./y.sh prepare`.
  3. Confirm the build dir is on a writable filesystem (not a read-only container mount) — relocate the download dir if needed.
  4. On Windows, disable real-time antivirus for the build tree or move off a OneDrive-synced path.
  5. 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

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


AI-assisted analysis of rust-lang/rust@22057b88b0 (2026-08-03). Data as JSON: /data/errors/0c136225191290fa.json. Report an issue: GitHub.