{"record":{"id":"f4f31e7c50ee7fab","repo":"jdx/mise","slug":"another-process-is-installing-the-mise-notificatio","errorCode":null,"errorMessage":"another process is installing the mise notification helper","messagePattern":"another process is installing the mise notification helper","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"src/system/history/notify/macos.rs","lineNumber":71,"sourceCode":"    let app = ensure_app(&crate::dirs::DATA.join(\"notifications\"))?;\n    Ok(notification_command(&app, title, body))\n}\n\nfn notification_command(app: &Path, title: &str, body: &str) -> Command {\n    let mut command = Command::new(executable(app));\n    command.args([title, body]);\n    command\n}\n\nfn ensure_app(root: &Path) -> Result<PathBuf> {\n    let app = app_path(root);\n    if complete(&app) {\n        return Ok(app);\n    }\n    crate::file::create_dir_all(root)?;\n    let mut lock = fslock::LockFile::open(&root.join(\"install.lock\"))?;\n    if !lock.try_lock()? {\n        bail!(\"another process is installing the mise notification helper\");\n    }\n    if complete(&app) {\n        return Ok(app);\n    }\n    let staging = tempfile::tempdir_in(root)?;\n    let staged = staging.path().join(\"mise.app\");\n    let contents = staged.join(\"Contents\");\n    std::fs::create_dir_all(contents.join(\"MacOS\"))?;\n    std::fs::create_dir_all(contents.join(\"Resources\"))?;\n    std::fs::create_dir_all(contents.join(\"_CodeSignature\"))?;\n    std::fs::write(executable(&staged), HELPER)?;\n    std::fs::set_permissions(executable(&staged), std::fs::Permissions::from_mode(0o755))?;\n    std::fs::write(contents.join(\"Info.plist\"), INFO)?;\n    std::fs::write(contents.join(\"Resources/mise.icns\"), ICON)?;\n    std::fs::write(\n        contents.join(\"_CodeSignature/CodeResources\"),\n        CODE_RESOURCES,\n    )?;","sourceCodeStart":53,"sourceCodeEnd":89,"githubUrl":"https://github.com/jdx/mise/blob/533346cc374382b41ec5ff70536252b2e96e725c/src/system/history/notify/macos.rs#L53-L89","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: parallel hooks both notify\nmise hook-env & mise hook-env &\n// after: single-shot guard\nflock /tmp/mise-notify.lock -c 'mise hook-env'","handlingStrategy":"retry","validationCode":"const { openSync, closeSync } = require('fs');\nexport function isHelperInstallLocked(lockPath: string): boolean {\n  try {\n    const fd = openSync(lockPath, 'wx'); // EEXIST means file exists; lock is held elsewhere\n    closeSync(fd);\n    return false;\n  } catch {\n    return true;\n  }\n}","typeGuard":null,"tryCatchPattern":"try {\n  ensureNotificationHelper();\n} catch (e) {\n  if (/another process is installing/.test(String(e))) {\n    await sleep(1000);\n    return ensureNotificationHelper(); // lock holder will have finished install\n  }\n  throw e;\n}","preventionTips":["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"],"tags":["concurrency","file-lock","macos","notification"],"backgroundTag":"file-already-exists","analyzedSha":"533346cc374382b41ec5ff70536252b2e96e725c","analyzedAt":"2026-09-17T13:35:38.149Z","contentChangedAt":"2026-09-17T13:35:38.149Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}