hashicorp/vagrant · error · Vagrant::Errors::EnvironmentLockedError

Vagrant attempted to acquire a lock named '%{name}', but thi

Error message

Vagrant attempted to acquire a lock named '%{name}', but this
lock is being held by another instance of Vagrant already. Please
wait and try again.

What it means

Raised as Vagrant::Errors::EnvironmentLockedError from Vagrant::Environment#lock (lib/vagrant/environment.rb:633) when flock(File::LOCK_EX | File::LOCK_NB) on data_dir/lock.<name>.lock keeps returning false (another Vagrant instance holds it) and opts[:retry] is false. With retry: true the method sleeps 0.2s and loops instead; the lock is per local-data-directory, so only Vagrants sharing the same .vagrant dir contend.

Source

Thrown at lib/vagrant/environment.rb:633

      # This allows multiple locks in the same process to be nested
      return yield if @locks[name] || opts[:noop]

      # The path to this lock
      lock_path = data_dir.join("lock.#{name}.lock")

      @logger.debug("Attempting to acquire process-lock: #{name}")
      lock("dotlock", noop: name == "dotlock", retry: true) do
        f = File.open(lock_path, "w+")
      end

      # The file locking fails only if it returns "false." If it
      # succeeds it returns a 0, so we must explicitly check for
      # the proper error case.
      while f.flock(File::LOCK_EX | File::LOCK_NB) === false
        @logger.warn("Process-lock in use: #{name}")

        if !opts[:retry]
          raise Errors::EnvironmentLockedError,
            name: name
        end

        sleep 0.2
      end

      @logger.info("Acquired process lock: #{name}")

      result = nil
      begin
        # Mark that we have a lock
        @locks[name] = true

        result = yield
      ensure
        # We need to make sure that no matter what this is always
        # reset to false so we don't think we have a lock when we
        # actually don't.

View on GitHub (pinned to 35f3160f4a)

Solutions

  1. Find the holder with `ps aux | grep vagrant` and wait for it to finish or stop it — flock is released automatically on process exit, so there are no stale locks to delete
  2. Retry the command once the other invocation completes
  3. Serialize access in CI (single job lane, or wrap commands in `flock /path/.vagrant/lock.global.lock vagrant ...`)
  4. If you call env.lock yourself, pass retry: true to block until the lock frees instead of failing fast

Example fix

# before
env.lock("global", retry: false) { env.cli(%w[up]) }

# after
env.lock("global", retry: true) { env.cli(%w[up]) }
Defensive patterns

Strategy: retry

Try / catch

def with_vagrant_lock(env, name, tries: 5, delay: 0.5)
  begin
    env.lock(name, retry: false) { yield }
  rescue Vagrant::Errors::EnvironmentLockedError
    tries -= 1
    raise if tries.zero?
    sleep delay
    delay *= 2
    retry
  end
end

Prevention

When it happens

Trigger: Two vagrant processes touching the same environment at once — e.g. `vagrant provision` while `vagrant up` runs — on a code path that calls env.lock(name) without retry: true; custom tooling invoking Environment#lock with retry: false; parallel CI jobs sharing one checkout and its .vagrant directory.

Common situations: Overlapping cron and interactive runs; a hung vagrant Ruby process still holding the flock; CI pipelines that start multiple vagrant stages concurrently on the same workspace.

Related errors


AI-assisted analysis of hashicorp/vagrant@35f3160f4a (2026-08-21). Data as JSON: /api/errors/0f93cb644aeb9667. Report an issue: GitHub.