jdx/mise · error
ditto failed copying
Error message
ditto failed copying {} to {} What it means
mise copies cask bundle contents into its private staging directory by invoking the macOS 'ditto' tool in a trusted directory context. When ditto exits with a non-zero status, mise wraps the failure in this error reporting the source path and the staging destination. Causes include disk-full, permission problems on the source bundle, or source files disappearing mid-copy.
Solutions
- Re-run the cask install; a transient source/IO failure often resolves on retry after the artifact is re-downloaded.
- Free disk space on the destination volume and check it is not read-only.
- Verify the source bundle path exists and is fully downloaded (clear the cache and re-download the cask).
- Check permissions/xattrs on the source files (dequarantine with xattr -dr com.apple.quarantine <path> if needed).
- Ensure 'ditto' is present and functional (it ships with macOS; a stripped-down environment may lack it).
Defensive patterns
Strategy: retry
Validate before calling
let src = std::path::Path::new(&from);
if !src.exists() {
eprintln!("source bundle missing before copy: {}", src.display());
std::process::exit(1);
}
let free = fs_free_bytes(&dest_volume);
if free < estimated_size {
eprintln!("insufficient disk space: {free} bytes free");
std::process::exit(1);
} Try / catch
match result {
Err(e) if e.to_string().contains("ditto failed copying") => {
// clear cache, free disk space, then retry install once
clear_brew_cache()?;
retry_install()?;
}
r => r?,
} Prevention
- Keep ample free disk space on the destination volume.
- Do not touch or clean source downloads while a cask is installing.
- Materialize iCloud-offloaded files before installing casks from those paths.
- Clear the brew cask cache after any interrupted install and retry.
- Ensure the standard macOS 'ditto' tool is available.
When it happens
Trigger: run_in_trusted_dir("ditto", [from, "."], destination) returns a failing exit status; the source bundle was deleted or renamed during install; target volume is full or read-only; source contains unreadable/locked files.
Common situations: Full APFS volume during large cask installs; downloads folder cleanup racing the install; iCloud-offloaded files that cannot be materialized; quarantine/xattr issues making source files unreadable; running out of disk in ~/Library/Caches.
Understand the failure class
Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.
Related errors
- ditto failed copying
- brew-cask: installer executable
- brew-cask: : pkg artifacts require pkgutil ids in uninstall…
- brew-cask: staging directory is not owned by the current…
- another process is installing the mise notification helper
AI-assisted analysis of jdx/mise@533346cc37 (2026-09-17).
Data as JSON: /api/errors/b23449dfa031b50d.
Report an issue: GitHub.
Appendix: source
Thrown at src/system/packages/brew/cask/mod.rs:2658
let destination = open_dir_nofollow_at(&dir, name).wrap_err_with(|| {
format!(
"brew-cask: cannot open staging directory {}",
Path::new(name).display()
)
})?;
let stat = nix::sys::stat::fstat(&destination)?;
if stat.st_uid != nix::unistd::geteuid().as_raw() {
bail!("brew-cask: staging directory is not owned by the current user");
}
// `ditto src dst` copies the *contents* of src into dst, so pointing it at
// the bound directory reproduces the bundle in place.
let status = run_in_trusted_dir(
"ditto",
&[from.as_os_str(), std::ffi::OsStr::new(".")],
&destination,
)?;
if !status.success() {
bail!(
"ditto failed copying {} to {}",
from.display(),
Path::new(name).display()
);
}
// Restore the bundle's own permissions, which the private staging mode hid.
if let Ok(metadata) = from.symlink_metadata() {
use std::os::unix::fs::PermissionsExt;
let mode = nix::sys::stat::Mode::from_bits_truncate(
metadata.permissions().mode() as nix::libc::mode_t
);
nix::sys::stat::fchmod(&destination, mode)?;
}
Ok(())
}
fn install_pkg(stage: &Path, pkg: &PkgArtifact) -> Result<()> {
let source = find_file_artifact(stage, &pkg.source)View on GitHub (pinned to 533346cc37)