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
- Wait for the other mise process to finish and retry — the race is transient and the lock releases on completion.
- Check for a stuck mise process (`pgrep -f mise`) holding the lock and kill it if hung.
- If a stale lock file remains and no mise process is running, remove the install.lock file in the notifications data directory and retry.
- 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
- Avoid launching many mise processes concurrently at shell startup
- Serialize notification-triggering hooks with an outer flock
- Check for hung mise processes before manually deleting install.lock
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
- the embedded notification helper is not Developer ID signed
- another history operation is running
- another history operation is running
- another setup sync or pull is running; retry shortly
- app target is outside an allowed Applications directory
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)