nikivdev/code · error

Refusing to overwrite {}

Error message

Refusing to overwrite {}

What it means

copy_dir_all recursively copies a directory tree, and before writing each entry it checks whether the corresponding target path already exists, bailing with 'Refusing to overwrite <path>' if so. This is an explicit non-destructive guarantee: the copier never clobbers existing files or directories, and it fails at the first collision instead of merging silently.

Source

Thrown at src/code.rs:857

        err.raw_os_error() == Some(libc::EXDEV)
    }
    #[cfg(not(unix))]
    {
        let _ = err;
        false
    }
}

fn copy_dir_all(from: &Path, to: &Path) -> Result<()> {
    fs::create_dir_all(to).with_context(|| format!("failed to create {}", to.display()))?;
    for entry in fs::read_dir(from).with_context(|| format!("failed to read {}", from.display()))? {
        let entry = entry?;
        let path = entry.path();
        let file_type = entry.file_type()?;
        let target = to.join(entry.file_name());

        if target.exists() {
            bail!("Refusing to overwrite {}", target.display());
        }

        if file_type.is_dir() {
            copy_dir_all(&path, &target)?;
        } else if file_type.is_file() {
            fs::copy(&path, &target)
                .with_context(|| format!("failed to copy {}", path.display()))?;
        } else if file_type.is_symlink() {
            let link_target = fs::read_link(&path)
                .with_context(|| format!("failed to read link {}", path.display()))?;
            copy_symlink(&link_target, &target)?;
        }
    }
    Ok(())
}

fn copy_symlink(target: &Path, dest: &Path) -> Result<()> {
    #[cfg(unix)]

View on GitHub (pinned to a747e741ae)

Solutions

  1. Delete or move the existing destination directory, then re-run the copy.
  2. Choose a fresh target path that does not exist.
  3. If a previous run failed midway, remove the partial output first — the copier is not resumable.
  4. Compare source and destination trees beforehand (e.g. `rsync -n`) to find which entries collide.

Example fix

// before
copy_dir_all(&template, &target)?; // target/config.toml already exists
// after
if target.exists() {
    fs::remove_dir_all(&target)?; // or pick a new target
}
copy_dir_all(&template, &target)?;
Defensive patterns

Strategy: try-catch

Validate before calling

fn target_is_clear(src: &Path, dst: &Path) -> bool {
    !dst.exists() || !walkdir::WalkDir::new(src).into_iter().any(|e| {
        e.map(|e| dst.join(e.file_name()).exists()).unwrap_or(true)
    })
}

Try / catch

match copy_dir_all(src, dst) {
    Err(e) if e.to_string().starts_with("Refusing to overwrite") => {
        let path = e.to_string().trim_start_matches("Refusing to overwrite ");
        eprintln!("Collision at {path}: remove the existing destination and retry.");
    }
    other => other?,
}

Prevention

When it happens

Trigger: Calling any of copy_dir_all's callers (new_from_template, new_project, migrate_project, migrate_to_path, move_dir, or copy_dir_all recursively) when the destination tree already contains any file or directory that the source tree would create — including a partially-created target left by a previous failed run (copy_dir_all recurses into itself, so a retry after a mid-copy failure hits this on the leftover files).

Common situations: Re-running `new` with a template into an existing folder; retrying a migration that crashed halfway, leaving partial output; template files colliding with files created by ensure_dir/other steps; case-insensitive filesystems colliding on names differing only by case.

Related errors


AI-assisted analysis of nikivdev/code@a747e741ae (2026-09-01). Data as JSON: /api/errors/c6b5223e1f7ee6f4. Report an issue: GitHub.