jdx/mise · warning

another process is installing the mise notification helper

Error message

another process is installing the mise notification helper

What it means

Installing/repairing the macOS notification helper app is guarded by an exclusive install.lock file in the notifications data directory. ensure_app bails when another process already holds the lock, preventing two mise processes from staging/creating the helper bundle concurrently and corrupting it.

Solutions

  1. Wait for the other mise process to finish and retry — the race is transient and the lock releases on completion.
  2. Check for a stuck mise process (`pgrep -f mise`) holding the lock and kill it if hung.
  3. If a stale lock file remains and no mise process is running, remove the install.lock file in the notifications data directory and retry.
  4. Serialize notification-triggering startup hooks so only one mise instance performs helper installation.

Example fix

// before: parallel hooks both notify
mise hook-env & mise hook-env &
// after: single-shot guard
flock /tmp/mise-notify.lock -c 'mise hook-env'
Defensive patterns

Strategy: retry

Validate before calling

const { openSync, closeSync } = require('fs');
export function isHelperInstallLocked(lockPath: string): boolean {
  try {
    const fd = openSync(lockPath, 'wx'); // EEXIST means file exists; lock is held elsewhere
    closeSync(fd);
    return false;
  } catch {
    return true;
  }
}

Try / catch

try {
  ensureNotificationHelper();
} catch (e) {
  if (/another process is installing/.test(String(e))) {
    await sleep(1000);
    return ensureNotificationHelper(); // lock holder will have finished install
  }
  throw e;
}

Prevention

When it happens

Trigger: ensure_app (called from notification and from repair/diagnostic paths) finds the app incomplete, then fails `lock.try_lock()` because another mise process currently holds install.lock while installing the helper.

Common situations: Multiple mise invocations racing at shell startup (e.g. hooks in several terminal sessions firing notifications simultaneously); a previous mise crashed leaving a stale lock held momentarily; CI or scripts launching parallel mise commands that both need the helper.

Understand the failure class

Background: "already exists" / EEXIST / FileAlreadyExistsException: what the 'file already exists' error means and how to fix it — this error's family across 37 libraries.

Related errors


AI-assisted analysis of jdx/mise@533346cc37 (2026-09-17). Data as JSON: /api/errors/f4f31e7c50ee7fab. Report an issue: GitHub.

Appendix: source

Thrown at src/system/history/notify/macos.rs:71

    let app = ensure_app(&crate::dirs::DATA.join("notifications"))?;
    Ok(notification_command(&app, title, body))
}

fn notification_command(app: &Path, title: &str, body: &str) -> Command {
    let mut command = Command::new(executable(app));
    command.args([title, body]);
    command
}

fn ensure_app(root: &Path) -> Result<PathBuf> {
    let app = app_path(root);
    if complete(&app) {
        return Ok(app);
    }
    crate::file::create_dir_all(root)?;
    let mut lock = fslock::LockFile::open(&root.join("install.lock"))?;
    if !lock.try_lock()? {
        bail!("another process is installing the mise notification helper");
    }
    if complete(&app) {
        return Ok(app);
    }
    let staging = tempfile::tempdir_in(root)?;
    let staged = staging.path().join("mise.app");
    let contents = staged.join("Contents");
    std::fs::create_dir_all(contents.join("MacOS"))?;
    std::fs::create_dir_all(contents.join("Resources"))?;
    std::fs::create_dir_all(contents.join("_CodeSignature"))?;
    std::fs::write(executable(&staged), HELPER)?;
    std::fs::set_permissions(executable(&staged), std::fs::Permissions::from_mode(0o755))?;
    std::fs::write(contents.join("Info.plist"), INFO)?;
    std::fs::write(contents.join("Resources/mise.icns"), ICON)?;
    std::fs::write(
        contents.join("_CodeSignature/CodeResources"),
        CODE_RESOURCES,
    )?;

View on GitHub (pinned to 533346cc37)