rust-lang/rust · error

failed to copy {src:?}->{dst:?}: {e}

Error message

failed to copy {src:?}->{dst:?}: {e}

What it means

Panics inside copy_dir_recursively when fs::copy fails to copy a regular file from src to dst. Like error 60 this runs during the prepare stage that stages rustc sources into the build target. The panic halts staging because every source file is expected to copy cleanly.

Source

Thrown at compiler/rustc_codegen_cranelift/build_system/utils.rs:222

            }
        }
    }
}

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;
        }
        let src = from.join(&filename);
        let dst = to.join(&filename);
        if entry.metadata().unwrap().is_dir() {
            fs::create_dir(&dst).unwrap_or_else(|e| panic!("failed to create {dst:?}: {e}"));
            copy_dir_recursively(&src, &dst);
        } else {
            fs::copy(&src, &dst).unwrap_or_else(|e| panic!("failed to copy {src:?}->{dst:?}: {e}"));
        }
    }
}

static IN_GROUP: AtomicBool = AtomicBool::new(false);
pub(crate) struct LogGroup {
    is_gha: bool,
}

impl LogGroup {
    pub(crate) fn guard(name: &str) -> LogGroup {
        let is_gha = env::var("GITHUB_ACTIONS").is_ok();

        assert!(!IN_GROUP.swap(true, Ordering::SeqCst));
        if is_gha {
            eprintln!("::group::{name}");
        }

View on GitHub (pinned to 22057b88b0)

Solutions

  1. Re-run `./y.rs prepare` from a clean checkout so the source tree is not mutating during the copy.
  2. Verify read permission on src: `ls -l <src>` (look for mode and owner).
  3. Check the dst filesystem is writable and has free space: `df -h <dst>` and `touch <dst-dir>/.probe`.
  4. On Windows, exclude the build directory from antivirus real-time scanning.

Example fix

// before
fs::copy(&src, &dst).unwrap_or_else(|e| panic!("failed to copy {src:?}->{dst:?}: {e}"));
// after
fs::copy(&src, &dst).unwrap_or_else(|e| match e.kind() {
    io::ErrorKind::AlreadyExists => { /* re-copy is fine, fall through */ fs::copy(&src, &dst).unwrap() }
    _ => panic!("failed to copy {src:?}->{dst:?}: {e}"),
});
Defensive patterns

Strategy: validation

Validate before calling

use std::path::Path;
fn ensure_copyable(src: &Path, dst: &Path) -> Result<(), String> {
    let sm = std::fs::metadata(src).map_err(|e| format!("src {:?} not readable: {}", src, e))?;
    if !sm.is_file() { return Err(format!("src {:?} is not a regular file", src)); }
    let parent = dst.parent().ok_or_else(|| format!("dst {:?} has no parent", dst))?;
    if !parent.exists() { return Err(format!("dst parent {:?} missing", parent)); }
    let pm = std::fs::metadata(parent).map_err(|e| format!("cannot stat dst parent: {}", e))?;
    if pm.permissions().readonly() { return Err(format!("dst parent {:?} is readonly", parent)); }
    if dst.exists() && dst.metadata().map(|m| m.permissions().readonly()).unwrap_or(false) {
        return Err(format!("dst {:?} exists and is readonly", dst));
    }
    Ok(())
}

Try / catch

use std::panic;
match panic::catch_unwind(|| fs::copy(src, dst)) {
    Ok(Ok(n)) => { /* copied n bytes */ }
    Ok(Err(e)) => return Err(format!("io copy failed: {}", e)),
    Err(_)    => { /* remove partial dst, surface as fatal build error */ }
}

Prevention

When it happens

Trigger: Reached for every non-directory entry while walking `from` during copy_dir_recursively. The panic message shows both src and dst plus the io::Error returned by std::fs::copy.

Common situations: src file is removed/renamed between read_dir and copy (TOCTOU, common with editor temp files or `.git` mutating underneath); dst is on a read-only or full filesystem; permission bits on src forbid reading (e.g. staged under a mode-0600 dir); cross-filesystem copy hits an immutable/append-only flag; antivirus/Defender locking the file on Windows.

Related errors


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