{"record":{"id":"b23449dfa031b50d","repo":"jdx/mise","slug":"ditto-failed-copying-to-b23449","errorCode":null,"errorMessage":"ditto failed copying {} to {}","messagePattern":"ditto failed copying (.+?) to (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/packages/brew/cask/mod.rs","lineNumber":2359,"sourceCode":"            .ok_or_else(|| eyre!(\"brew-cask: generic artifact target has no filename\"))?;\n        nix::fcntl::renameat(&parent.fd, from_name, &parent.fd, to_name)?;\n        Ok(())\n    }\n    #[cfg(not(unix))]\n    {\n        let _ = expected_parent;\n        file::rename(from, to)\n    }\n}\n\nfn ditto(from: &Path, to: &Path) -> Result<()> {\n    let status = std::process::Command::new(\"ditto\")\n        .arg(from)\n        .arg(to)\n        .status()\n        .wrap_err(\"failed to run ditto\")?;\n    if !status.success() {\n        bail!(\n            \"ditto failed copying {} to {}\",\n            from.display(),\n            to.display()\n        );\n    }\n    Ok(())\n}\n\n/// Run a helper with its working directory bound to `dir` via `fchdir`, so\n/// relative arguments resolve from that exact directory inode.\n///\n/// Passing a pathname to a subprocess would let it re-resolve every component,\n/// which a same-uid replacement can redirect. `fchdir` in the child pins\n/// resolution to the descriptor mise already verified, so relative names cannot\n/// escape the validated application directory.\n#[cfg(unix)]\nfn run_in_trusted_dir<Fd: std::os::fd::AsFd>(\n    program: &str,","sourceCodeStart":2341,"sourceCodeEnd":2377,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/system/packages/brew/cask/mod.rs#L2341-L2377","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// diagnose manually\n# ditto \"$HOME/.cache/.../App.app\" \"$MISE_PREFIX/apps/App.app\"; echo $?\n// fix permissions then retry\nchmod -R u+w \"$MISE_PREFIX/apps\" && mise install <cask>","handlingStrategy":"try-catch","validationCode":"if Command::new(\"ditto\").arg(\"--version\").output().is_err() {\n    panic!(\"ditto is unavailable; cask bundle copy will fail\");\n}\n// also check disk space and writability of destination","typeGuard":null,"tryCatchPattern":"match result {\n    Err(e) if e.to_string().starts_with(\"ditto failed copying\") => {\n        eprintln!(\"check disk space, permissions, and run ditto manually with the printed paths\");\n    }\n    Err(e) => return Err(e),\n    Ok(v) => v,\n}","preventionTips":["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"],"tags":["macos","ditto","file-copy","brew-cask"],"backgroundTag":"command-not-found","analyzedSha":"afd2eddd3a50c16190efc1c7e94404b48f72af57","analyzedAt":"2026-09-09T01:38:25.179Z","contentChangedAt":"2026-09-09T01:38:25.179Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}