hyperledger/fabric · error

lock is already acquired on file %s

Error message

lock is already acquired on file %s

What it means

FileLock.Lock returns errors.Errorf when leveldb.OpenFile on the lock file fails with syscall.EAGAIN, meaning another process already holds the lock. This is the only error Lock returns normally; all other open failures panic. It is the expected 'lock is taken' signal for cross-process mutual exclusion.

Source

Thrown at common/ledger/util/leveldbhelper/leveldb_helper.go:216

// Lock acquire a file lock. We achieve this by opening
// a db for the given filePath. Internally, leveldb acquires a
// file lock while opening a db. If the db is opened again by the same or
// another process, error would be returned. When the db is closed
// or the owner process dies, the lock would be released and hence
// the other process can open the db. We exploit this leveldb
// functionality to acquire and release file lock as the leveldb
// supports this for Windows, Solaris, and Unix.
func (f *FileLock) Lock() error {
	dbOpts := &opt.Options{}
	var err error
	var dirEmpty bool
	if dirEmpty, err = fileutil.CreateDirIfMissing(f.filePath); err != nil {
		panic(fmt.Sprintf("Error creating dir if missing: %s", err))
	}
	dbOpts.ErrorIfMissing = !dirEmpty
	db, err := leveldb.OpenFile(f.filePath, dbOpts)
	if err != nil && err == syscall.EAGAIN {
		return errors.Errorf("lock is already acquired on file %s", f.filePath)
	}
	if err != nil {
		panic(fmt.Sprintf("Error acquiring lock on file %s: %s", f.filePath, err))
	}

	// only mutate the lock db reference AFTER validating that the lock was held.
	f.db = db

	return nil
}

// Determine if the lock is currently held open.
func (f *FileLock) IsLocked() bool {
	return f.db != nil
}

// Unlock releases a previously acquired lock. We achieve this by closing
// the previously opened db. FileUnlock can be called multiple times.

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Ensure only one process uses this file lock path (check running processes / container replicas).
  2. Wait and retry until the current holder releases the lock.
  3. Kill or wait for the stale holder process; the OS lock frees when the holder exits.
  4. Compare with errors.Is(err, ...) on the returned error to distinguish 'lock held' from other failures (which panic instead).

Example fix

// before
fileLock.Lock()
// after
if err := fileLock.Lock(); err != nil {
  if strings.Contains(err.Error(), "lock is already acquired") {
    return errors.New("another process holds the file lock; retry later")
  }
  return err
}
Defensive patterns

Strategy: retry

Validate before calling

// check no other holder before locking (best-effort)
if procHolding(lockFilePath) != nil {
  return errors.New("another process holds the file lock")
}

Type guard

func isLockHeldErr(err error) bool {
  return err != nil && strings.Contains(err.Error(), "lock is already acquired on file")
}

Try / catch

for i := 0; i < maxAttempts; i++ {
  err := fileLock.Lock()
  if err == nil { break }
  if isLockHeldErr(err) { time.Sleep(retryInterval); continue }
  return err
}

Prevention

When it happens

Trigger: A second process (or a leftover process) attempts Lock on the same file path while the first holder is alive; a stale lock file is held by a hung process that never released it.

Common situations: Starting two peer/leader-election processes against the same data directory; a crashed-but-not-exited process still holding the OS lock; container restarts where the old PID lingers.

Related errors


AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04). Data as JSON: /api/errors/ad8c5e8eec5bb872. Report an issue: GitHub.