Skip to content

Improve update lock error when held by git fsmonitor #22828

Description

@joshka

Provide a detailed description of the proposed feature

Verification

TL;DR: the current update-lock error can be wrong for this case. Homebrew says another brew update process is running, but the lock holder was actually a detached git fsmonitor--daemon process from a Homebrew tap checkout. The message also does not expose enough information to understand why it is wrong or how to diagnose the real lock holder.

The crisp question is: if the update lock is held only by a detached git fsmonitor--daemon, is it safe for Homebrew to ignore that lock holder, or should Homebrew instead disable/stop fsmonitor for its own repos/taps? Either way, the current error should expose the lock path and holder process so users can diagnose why the message is wrong.

I have seen this error intermittently for years:

Error: Another `brew update` process is already running.
Please wait for it to finish or terminate it to continue.

I always assumed it was some random local breakage in my setup. I only got to the root cause today because an agent helped inspect the lock holder directly.

In this case, there was no active brew update process. The update lock was being held by a detached git fsmonitor--daemon process from a Homebrew tap checkout.

The lock holder was visible with:

lsof /opt/homebrew/var/homebrew/locks/update

Output:

COMMAND   PID   USER   FD   TYPE DEVICE SIZE/OFF      NODE NAME
git     95197 joshka  200w   REG   ...        0 ... /opt/homebrew/var/homebrew/locks/update

Inspecting that PID showed:

/opt/homebrew/opt/git/libexec/git-core/git fsmonitor--daemon run --detach --ipc-threads=8

It was associated with a Homebrew tap checkout:

/opt/homebrew/Library/Taps/homebrew/homebrew-cask/.git/fsmonitor--daemon.ipc

My global Git config had:

core.fsmonitor true
core.untrackedcache true

Disabling fsmonitor for the Homebrew tap repos and stopping the daemon made brew update work immediately:

git -C /opt/homebrew/Library/Taps/homebrew/homebrew-core config core.fsmonitor false
git -C /opt/homebrew/Library/Taps/homebrew/homebrew-cask config core.fsmonitor false
kill 95197
brew update

After that, brew update ran normally.

The current message gives a false start: it says another brew update process is running, so the natural thing to look for is another brew process. In this case, there was no such process.

The message also does not say:

  • which lock could not be acquired
  • where that lock file is
  • which process currently holds it
  • whether the holder is actually a Homebrew process
  • what command can be used to inspect the holder

To diagnose the actual cause, a user needs to know:

  • Homebrew uses lock files under $(brew --prefix)/var/homebrew/locks
  • the relevant lock is update
  • lsof can identify the holder
  • Git fsmonitor can run as a detached daemon
  • global core.fsmonitor=true can affect Homebrew tap checkouts
  • the daemon may keep the update lock file descriptor open after the original Homebrew process is gone

That is a lot of internal context to need from a short error message. I had seen this for years without it ever bubbling up to a reasonably diagnosable issue.

I do not think git fsmonitor--daemon itself should be treated as inherently suspicious. Git documents core.fsmonitor=true as enabling the built-in filesystem monitor daemon for a working directory, and says it can speed up commands like git status in repositories with many files.

So this may be a reasonable global Git configuration rather than a broken user setup. I barely remembered enabling it; it was likely from general Git performance advice.

That said, I am not sure whether fsmonitor is safe or compatible with the way Homebrew uses Git internally when it is enabled globally by the user.

Current Homebrew code appears to partially account for this already. Library/Homebrew/cmd/update.sh contains this comment:

# Git's fsmonitor prevents the release of our locks
git config --bool core.fsmonitor false

However, that happens after lock update, and it appears to apply only in a narrow repository case rather than to all Homebrew taps. In my case, the lock was held by fsmonitor from homebrew-cask.

At minimum, the error should not assert that another brew update process is running unless the lock holder is actually a Homebrew update process.

When Homebrew cannot acquire the update lock, please include the lock path and, when available, the PID/command currently holding it.

For example:

Error: Homebrew could not acquire the update lock.

Lock: /opt/homebrew/var/homebrew/locks/update
Holder: PID 95197, git fsmonitor--daemon run --detach --ipc-threads=8

If the holder is git fsmonitor--daemon, possible next steps could be one of:

  1. If it is safe, ignore a lock held only by a detached git fsmonitor--daemon.
  2. If fsmonitor is not compatible with Homebrew’s Git usage, override user/global fsmonitor configuration in Homebrew’s own repos/taps, or stop fsmonitor before taking the update lock.
  3. Keep the current lock behavior, but report the actual holder and suggest disabling fsmonitor for Homebrew repos/taps.

Option 3 seems like the safest minimum change. For example, Homebrew could suggest:

git -C "$(brew --repo homebrew/core)" config core.fsmonitor false
git -C "$(brew --repo homebrew/cask)" config core.fsmonitor false

A brew doctor check for Homebrew lock files held by long-running detached git fsmonitor--daemon processes could also help users who keep seeing this over time.

The important part is that the current message leaves the user looking for another brew update process, which is not what is holding the lock in this case.

This appears related to the older discussion in #13521, but the user-facing failure mode remains difficult to diagnose on current Homebrew.

What is the motivation for the feature?

This would make a long-running and misleading Homebrew lock failure diagnosable from the error itself.

Right now, a user who sees Another brew update process is already running is sent looking for another brew update process. If the real holder is a detached git fsmonitor--daemon, that search does not explain the failure. The user has to know Homebrew’s internal lock path and use lsof to discover the actual holder.

This is especially confusing because core.fsmonitor=true is a reasonable Git performance setting, not an obviously broken Homebrew-specific configuration.

How will the feature be relevant to at least 90% of Homebrew users?

The specific git fsmonitor--daemon case may not affect 90% of users, but clearer lock-holder diagnostics are broadly useful whenever Homebrew cannot acquire a lock. Any user who hits a Homebrew lock error would benefit from seeing which lock is held and which process holds it.

This also reduces support/debugging load because users can distinguish an actual concurrent Homebrew process from a detached helper or other unexpected process.

What alternatives to the feature have been considered?

  • Do nothing: users keep seeing a misleading error and need internal Homebrew knowledge to diagnose it.
  • Document the workaround only: helpful, but still hard to find when the immediate error does not expose the lock path or holder.
  • Tell users to disable Git fsmonitor globally: too broad, because fsmonitor is a reasonable Git performance feature.
  • Disable fsmonitor only for Homebrew repos/taps: this may be right if fsmonitor is incompatible with Homebrew’s Git usage, but I am looking for maintainer confirmation on that.
  • Ignore locks held only by detached git fsmonitor--daemon: this may be the best behavior if it can be proven safe, but I do not know enough about Homebrew’s lock semantics to assert that.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions