jdx/mise · warning
another process is installing the mise notification helper
Error message
another process is installing the mise notification helper
What it means
ensure_app stages and installs the notification helper app into the data directory under an install.lock fslock. When a complete app is not already present and another process holds the lock, it bails out so only one installer runs at a time, preventing two processes from corrupting the staged bundle.
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 afd2eddd3a)
Solutions
- Retry after a short delay — once the other process finishes installing, complete() succeeds and the lock path is skipped.
- Check for a hung process holding install.lock (lsof on the data dir) and wait or kill it.
- If a crashed installer left a partial bundle, remove the incomplete app in the data dir and let the next call repair it.
- Serialize notification setup in your scripts (only one mise process should install the helper first).
Example fix
// before: parallel notification calls racing mise notify & mise notify & // after: serialize first-time install mise notify && mise notify # or wait for the first install to complete
Defensive patterns
Strategy: retry
Validate before calling
let lock_free = !std::path::Path::new(&data_dir).join("notifications/install.lock")
.metadata().map(|m| m.len() > 0).unwrap_or(false); // advisory only; rely on retry Try / catch
for attempt in 0..5 {
match ensure_app(root) {
Err(e) if e.to_string().contains("another process is installing") => {
std::thread::sleep(Duration::from_millis(500 * (attempt + 1)));
}
other => { other?; break; }
}
} Prevention
- Let the first mise invocation install the helper before firing parallel notifications.
- Avoid sharing one data directory across concurrent CI jobs.
- Clean up partial installs after a crashed first run.
When it happens
Trigger: Concurrent calls to notification()/ensure_app (e.g. two mise processes emitting notifications) where the app is absent or incomplete and both race to acquire root/install.lock; a stale/crashed process briefly holding the lock.
Common situations: Multiple shells or scripts firing notifications simultaneously on first run; CI or parallel test runs sharing the same data directory; an interrupted first installation leaving an incomplete app so every process takes the lock path.
Related errors
- the embedded notification helper is not Developer ID signed
- `defaults {display}` failed: {}
- `defaults {}` failed: {}
- brew-cask:{}: Homebrew took ownership of this cask while ins
- brew-cask: refusing elevated operation because target appear
AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09).
Data as JSON: /api/errors/f4f31e7c50ee7fab.
Report an issue: GitHub.