{"record":{"id":"99341048542be7be","repo":"NousResearch/hermes-agent","slug":"running-ditto-e","errorCode":null,"errorMessage":"running ditto: {e}","messagePattern":"running ditto: (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"apps/bootstrap-installer/src-tauri/src/update.rs","lineNumber":1046,"sourceCode":"            target_app.display()\n        ),\n    );\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()","sourceCodeStart":1028,"sourceCodeEnd":1064,"githubUrl":"https://github.com/NousResearch/hermes-agent/blob/c896c09c42910c584c4c7d2325b58c14713ea42c/apps/bootstrap-installer/src-tauri/src/update.rs#L1028-L1064","documentation":"macOS-only failure in the bundle-copy step: tokio could not even spawn `/usr/bin/ditto` to copy the rebuilt .app into the staged <target>.hermes-update-new path. This is a spawn/OS-level error (ditto missing, fork failure), distinct from ditto running and exiting non-zero, which is error 555.","triggerScenarios":"/usr/bin/ditto absent on a gutted/locked-down macOS (rare, SIP-modified or non-standard system); process/resource limits hit (fork: resource temporarily unavailable); a security tool blocking execution of the child from the updater.","commonSituations":"Heavily hardened or MDM-locked Macs restricting child process execution; extremely low file descriptors/memory during the update; running the updater inside a restricted sandbox container where /usr/bin is not visible.","solutions":["Run `/usr/bin/ditto --help` in Terminal to confirm ditto exists and executes on that machine.","Free system resources / raise ulimits and retry.","If MDM/security software blocks it, exempt the updater or perform the update manually per the docs.","Check the error text after the colon — it names the exact OS errno."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"fn ditto_available() -> bool {\n    std::path::Path::new(\"/usr/bin/ditto\").exists()\n        && std::process::Command::new(\"/usr/bin/ditto\").arg(\"-h\").output().is_ok()\n}\n\nif !ditto_available() {\n    eprintln!(\"/usr/bin/ditto unavailable — cannot stage the app update on this system.\");\n}","typeGuard":null,"tryCatchPattern":"match Command::new(\"/usr/bin/ditto\").arg(&rebuilt_app).arg(&tmp).status().await {\n    Ok(st) if st.success() => { /* proceed to swap */ }\n    Ok(st) => Err(anyhow!(\"ditto failed while copying updated app into {}\", tmp.display())),\n    Err(e) => Err(anyhow!(\"running ditto: {e} — is /usr/bin/ditto present and executable?\")),\n}","preventionTips":["On hardened/MDM Macs, confirm /usr/bin/ditto executes from your updater context before shipping.","Watch system resource limits (fd/memory) when chaining many child processes."],"tags":["macos","child-process","update","bootstrap-installer"],"backgroundTag":null,"analyzedSha":"c896c09c42910c584c4c7d2325b58c14713ea42c","analyzedAt":"2026-08-14T17:18:01.089Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}