{"id":"0c136225191290fa","repo":"rust-lang/rust","slug":"failed-to-remove-path-err","errorCode":null,"errorMessage":"Failed to remove {path}: {err}","messagePattern":"Failed to remove (.+?): (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"compiler/rustc_codegen_cranelift/build_system/prepare.rs","lineNumber":179,"sourceCode":"\n    pub(crate) fn patch(&self, dirs: &Dirs) {\n        self.verify_checksum(dirs);\n        apply_patches(\n            dirs,\n            self.patch_name,\n            &self.download_dir(dirs),\n            &self.source_dir().to_path(dirs),\n        );\n    }\n}\n\nfn clone_repo(download_dir: &Path, repo: &str, rev: &str, submodules: &[&str]) {\n    eprintln!(\"[CLONE] {}\", repo);\n\n    match fs::remove_dir_all(download_dir) {\n        Ok(()) => {}\n        Err(err) if err.kind() == io::ErrorKind::NotFound => {}\n        Err(err) => panic!(\"Failed to remove {path}: {err}\", path = download_dir.display()),\n    }\n\n    // Ignore exit code as the repo may already have been checked out\n    git_command(None, \"clone\").arg(repo).arg(download_dir).spawn().unwrap().wait().unwrap();\n\n    let mut clean_cmd = git_command(download_dir, \"checkout\");\n    clean_cmd.arg(\"--\").arg(\".\");\n    spawn_and_wait(clean_cmd);\n\n    let mut checkout_cmd = git_command(download_dir, \"checkout\");\n    checkout_cmd.arg(\"-q\").arg(rev);\n    spawn_and_wait(checkout_cmd);\n\n    if !submodules.is_empty() {\n        let mut submodule_cmd = git_command(download_dir, \"submodule\");\n        submodule_cmd.arg(\"update\").arg(\"--init\");\n        submodule_cmd.args(submodules);\n        spawn_and_wait(submodule_cmd);","sourceCodeStart":161,"sourceCodeEnd":197,"githubUrl":"https://github.com/rust-lang/rust/blob/22057b88b091743bc0fd8d592a9264f0a6951403/compiler/rustc_codegen_cranelift/build_system/prepare.rs#L161-L197","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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`."],"exampleFix":"# before — running prepare with permission-denied stale checkout\n./y.sh prepare\n# panics: Failed to remove /path/download: Permission denied (os error 13)\n\n# after — fix ownership then re-run\nsudo chown -R $USER:$USER /path/download\nrm -rf /path/download\n./y.sh prepare","handlingStrategy":"retry","validationCode":"// prepare.rs remove failure — usually transient (file busy/locked) or a\n// permissions issue. Pre-flight check before invoking the build step:\nuse std::path::Path;\nfn removable(p: &Path) -> std::io::Result<()> {\n    let md = std::fs::metadata(p)?;\n    if md.permissions().readonly() {\n        return Err(std::io::Error::new(std::io::ErrorKind::PermissionDenied, \"readonly\"));\n    }\n    Ok(())\n}\n// Call removable(&path) before `./y.sh prepare`; fix perms or unlock, then retry.","typeGuard":null,"tryCatchPattern":"// The build step runs as a child process and panics on IO failure,\n// so 'catch' = inspect the child exit status and retry with backoff.\nuse std::{process::Command, thread, time::Duration};\nfn run_prepare_retry(n: u32) -> bool {\n    for attempt in 0..n {\n        let st = Command::new(\"./y.sh\").arg(\"prepare\").status();\n        match st {\n            Ok(s) if s.success() => return true,\n            _ if attempt + 1 < n => { thread::sleep(Duration::from_secs(2 << attempt)); }\n            _ => return false,\n        }\n    }\n    false\n}","preventionTips":["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"],"tags":["rustc","cranelift","build-system","io","filesystem"],"analyzedSha":"22057b88b091743bc0fd8d592a9264f0a6951403","analyzedAt":"2026-08-03T08:09:25.915Z","schemaVersion":2}