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
- Ensure only one process uses this file lock path (check running processes / container replicas).
- Wait and retry until the current holder releases the lock.
- Kill or wait for the stale holder process; the OS lock frees when the holder exits.
- 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
- Run exactly one process per lock path (check replicas/PIDs)
- Clean up hung processes that hold the lock before restarting
- Use bounded retry-with-backoff around Lock
- Remember other open failures panic — wrap Lock with recover
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
- Error creating dir if missing: %s
- error while trying to see if the leveldb at path [%s] is emp
- error retrieving leveldb key [%#v]
- error writing leveldb key [%#v]
- error deleting leveldb key [%#v]
AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04).
Data as JSON: /api/errors/ad8c5e8eec5bb872.
Report an issue: GitHub.