jdx/mise · error
ditto failed copying {} to {}
Error message
ditto failed copying {} to {} What it means
On macOS, bundle-style cask artifacts are copied with the `ditto` command, which preserves metadata/resource forks. The library runs `ditto <from> <to>` and treats a non-zero exit status as a fatal copy failure, including both paths in the message for diagnosis.
Source
Thrown at src/system/packages/brew/cask/mod.rs:2359
.ok_or_else(|| eyre!("brew-cask: generic artifact target has no filename"))?;
nix::fcntl::renameat(&parent.fd, from_name, &parent.fd, to_name)?;
Ok(())
}
#[cfg(not(unix))]
{
let _ = expected_parent;
file::rename(from, to)
}
}
fn ditto(from: &Path, to: &Path) -> Result<()> {
let status = std::process::Command::new("ditto")
.arg(from)
.arg(to)
.status()
.wrap_err("failed to run ditto")?;
if !status.success() {
bail!(
"ditto failed copying {} to {}",
from.display(),
to.display()
);
}
Ok(())
}
/// Run a helper with its working directory bound to `dir` via `fchdir`, so
/// relative arguments resolve from that exact directory inode.
///
/// Passing a pathname to a subprocess would let it re-resolve every component,
/// which a same-uid replacement can redirect. `fchdir` in the child pins
/// resolution to the descriptor mise already verified, so relative names cannot
/// escape the validated application directory.
#[cfg(unix)]
fn run_in_trusted_dir<Fd: std::os::fd::AsFd>(
program: &str,View on GitHub (pinned to afd2eddd3a)
Solutions
- Re-run the install after checking `df -h` for free disk space
- Check permissions/ownership of the destination directory and the source bundle
- Re-download/re-extract the cask payload to rule out corruption
- Run the ditto command manually with the printed paths to see the underlying ditto error
Example fix
// diagnose manually # ditto "$HOME/.cache/.../App.app" "$MISE_PREFIX/apps/App.app"; echo $? // fix permissions then retry chmod -R u+w "$MISE_PREFIX/apps" && mise install <cask>
Defensive patterns
Strategy: try-catch
Validate before calling
if Command::new("ditto").arg("--version").output().is_err() {
panic!("ditto is unavailable; cask bundle copy will fail");
}
// also check disk space and writability of destination Try / catch
match result {
Err(e) if e.to_string().starts_with("ditto failed copying") => {
eprintln!("check disk space, permissions, and run ditto manually with the printed paths");
}
Err(e) => return Err(e),
Ok(v) => v,
} Prevention
- Keep enough free disk space for large app bundles
- Ensure destination staging dirs are writable and user-owned
- Re-download payloads when extraction errors appear
- Note: ditto is macOS-only; on other platforms a different copy path is used
When it happens
Trigger: The spawned `ditto` process exits non-zero while copying a staged cask bundle (or contents into the destination staging dir) — source missing mid-copy, destination not writable, disk full, or ditto not functioning.
Common situations: Full APFS disk during a large app install; destination staging dir owned by another user or read-only; interrupted/removed source; corrupted download being extracted.
Related errors
- ditto failed copying {} to {}
- brew-cask: staging directory is not owned by the current use
- brew-cask: structured copy source must resolve to exactly on
- brew-cask: structured copy source '{}' was not found
- brew-cask: structured directory copy requires recursive=true
AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09).
Data as JSON: /api/errors/b23449dfa031b50d.
Report an issue: GitHub.