{"record":{"id":"d3b423dc39ed1ca7","repo":"NousResearch/hermes-agent","slug":"ditto-failed-while-copying-updated-app-into","errorCode":null,"errorMessage":"ditto failed while copying updated app into {}","messagePattern":"ditto failed while copying updated app into (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"apps/bootstrap-installer/src-tauri/src/update.rs","lineNumber":1048,"sourceCode":"    );\n\n    if let Some(parent) = target_app.parent() {\n        tokio::fs::create_dir_all(parent).await?;\n    }\n    let tmp = PathBuf::from(format!(\"{}.hermes-update-new\", target_app.display()));\n    let old = PathBuf::from(format!(\"{}.hermes-update-old\", target_app.display()));\n    remove_dir_if_exists(&tmp).await;\n    remove_dir_if_exists(&old).await;\n\n    let ditto = Command::new(\"/usr/bin/ditto\")\n        .arg(&rebuilt_app)\n        .arg(&tmp)\n        .current_dir(crate::paths::hermes_home())\n        .status()\n        .await\n        .map_err(|e| anyhow!(\"running ditto: {e}\"))?;\n    if !ditto.success() {\n        return Err(anyhow!(\n            \"ditto failed while copying updated app into {}\",\n            tmp.display()\n        ));\n    }\n\n    // Atomic-as-possible swap with rollback. Extracted so the invariant\n    // (target is never left deleted-with-no-replacement) can be unit-tested\n    // without ditto / a real .app bundle.\n    swap_in_new_bundle(&tmp, target_app, &old).await?;\n\n    let _ = Command::new(\"/usr/bin/xattr\")\n        .arg(\"-dr\")\n        .arg(\"com.apple.quarantine\")\n        .arg(target_app)\n        .current_dir(crate::paths::hermes_home())\n        .status()\n        .await;\n","sourceCodeStart":1030,"sourceCodeEnd":1066,"githubUrl":"https://github.com/NousResearch/hermes-agent/blob/c896c09c42910c584c4c7d2325b58c14713ea42c/apps/bootstrap-installer/src-tauri/src/update.rs#L1030-L1066","documentation":"macOS-only failure: /usr/bin/ditto spawned successfully but exited non-zero while copying the rebuilt bundle into the staged <target>.hermes-update-new directory. Common ditto exit causes are source unreadable, destination volume issues, or metadata (code signing) errors during the copy.","triggerScenarios":"The rebuilt .app under apps/desktop/release was moved/deleted between rebuild and copy; disk full on the target volume; copying across filesystems (staging on a different volume than /Applications) with metadata failures; source bundle with invalid signing data; target path's parent missing or read-only.","commonSituations":"Disk nearly full on the /Applications volume; the rebuild output got cleaned up by another process; a symlinked/nonstandard Applications dir; damaged code-signing post-build.","solutions":["Free space on the target volume and retry the update.","Verify the rebuilt bundle still exists under install_root/apps/desktop/release and opens manually.","Reproduce manually: `/usr/bin/ditto <rebuilt.app> /tmp/test-copy.app` and read ditto's own error output.","If signing/metadata is the issue, rebuild the desktop app cleanly and retry."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Pre-flight: source bundle readable, target volume has room for a second copy.\nfn copy_precheck(src: &std::path::Path, dest_parent: &std::path::Path) -> std::io::Result<()> {\n    let meta = std::fs::metadata(src)?;\n    let need = meta.len() * 2; // staged copy + final\n    let avail = fs2::available_space(dest_parent)?;\n    if avail < need { return Err(std::io::Error::new(std::io::ErrorKind::StorageFull, \"not enough space\")); }\n    Ok(())\n}","typeGuard":null,"tryCatchPattern":"if !ditto.success() {\n    // tmp was fully staged or partially written; it is cleaned up by the caller's remove_dir_if_exists.\n    return Err(anyhow!(\n        \"ditto failed (exit {:?}) while copying updated app into {}. Original app is untouched.\",\n        ditto.code(), tmp.display()\n    ));\n}","preventionTips":["Ensure at least 2x the app-bundle size free on the target volume before updating.","Verify the rebuilt bundle exists and is stable (no concurrent cleanup) before the copy step.","Keep HERMES_HOME staging and the target .app on the same volume."],"tags":["macos","filesystem","update","bootstrap-installer"],"backgroundTag":null,"analyzedSha":"c896c09c42910c584c4c7d2325b58c14713ea42c","analyzedAt":"2026-08-14T17:18:01.089Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}