NousResearch/hermes-agent · error · anyhow::Error

desktop rebuild succeeded but no Hermes.app was found under

Error message

desktop rebuild succeeded but no Hermes.app was found under {}

What it means

macOS-only failure in install_macos_app_update: the `hermes desktop --build-only` rebuild reported success, but resolve_hermes_desktop_app() found no Hermes.app under <install_root>/apps/desktop/release. The updater will not guess a destination, so it aborts rather than installing a nonexistent bundle.

Source

Thrown at apps/bootstrap-installer/src-tauri/src/update.rs:998

    }
    None
}

#[cfg(target_os = "macos")]
async fn install_macos_app_update(
    app: &AppHandle,
    install_root: &Path,
    target_app: &Path,
) -> Result<PathBuf> {
    if target_app.extension().and_then(|e| e.to_str()) != Some("app") {
        return Err(anyhow!(
            "refusing to install update into non-app path: {}",
            target_app.display()
        ));
    }

    let rebuilt_app = crate::bootstrap::resolve_hermes_desktop_app(install_root).ok_or_else(|| {
        anyhow!(
            "desktop rebuild succeeded but no Hermes.app was found under {}",
            install_root.join("apps").join("desktop").join("release").display()
        )
    })?;

    let same = match (rebuilt_app.canonicalize(), target_app.canonicalize()) {
        (Ok(a), Ok(b)) => a == b,
        _ => rebuilt_app == target_app,
    };
    if same {
        emit_log(
            app,
            Some("install"),
            LogStream::Stdout,
            &format!(
                "[update] rebuilt app is already the launch target: {}",
                target_app.display()
            ),

View on GitHub (pinned to c896c09c42)

Solutions

  1. List <install_root>/apps/desktop/release — see what was actually produced and where the .app landed.
  2. If the bundle is elsewhere (config-driven), point the build config back to the default release path or fix resolve_hermes_desktop_app's search set.
  3. Clean-rebuild the desktop app (`hermes desktop --build-only`) and confirm Hermes.app exists before retrying the update.
Defensive patterns

Strategy: validation

Validate before calling

// After `--build-only`, assert the expected artifact exists before installing.
let expected = install_root.join("apps").join("desktop").join("release");
let bundle = crate::bootstrap::resolve_hermes_desktop_app(&install_root);
if bundle.is_none() {
    eprintln!("no Hermes.app under {} — check where electron-builder emitted the bundle", expected.display());
}

Type guard

fn rebuilt_bundle_present(install_root: &std::path::Path) -> Option<std::path::PathBuf> {
    let app = crate::bootstrap::resolve_hermes_desktop_app(install_root)?;
    app.join("Contents").join("MacOS").is_dir().then_some(app)
}

Prevention

When it happens

Trigger: The desktop build succeeded but emitted its bundle to a non-default release directory (custom build config changed the output path); a partial build wrote only some artifacts; the release dir was cleaned between build and install; bundle name changed from Hermes.app.

Common situations: Developer customization of electron-builder output paths; a build that skipped the macOS bundle target (built only the JS side); race with disk-cleanup tools removing large build outputs; renaming the product without updating the resolver.

Related errors


AI-assisted analysis of NousResearch/hermes-agent@c896c09c42 (2026-08-14). Data as JSON: /api/errors/07ba1f02b2d7140c. Report an issue: GitHub.