{"record":{"id":"227b45d4b0cecd91","repo":"hyperledger/fabric","slug":"error-acquiring-lock-on-file-s-s","errorCode":null,"errorMessage":"Error acquiring lock on file %s: %s","messagePattern":"Error acquiring lock on file (.+?): (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"common/ledger/util/leveldbhelper/leveldb_helper.go","lineNumber":219,"sourceCode":"// another process, error would be returned. When the db is closed\n// or the owner process dies, the lock would be released and hence\n// the other process can open the db. We exploit this leveldb\n// functionality to acquire and release file lock as the leveldb\n// supports this for Windows, Solaris, and Unix.\nfunc (f *FileLock) Lock() error {\n\tdbOpts := &opt.Options{}\n\tvar err error\n\tvar dirEmpty bool\n\tif dirEmpty, err = fileutil.CreateDirIfMissing(f.filePath); err != nil {\n\t\tpanic(fmt.Sprintf(\"Error creating dir if missing: %s\", err))\n\t}\n\tdbOpts.ErrorIfMissing = !dirEmpty\n\tdb, err := leveldb.OpenFile(f.filePath, dbOpts)\n\tif err != nil && err == syscall.EAGAIN {\n\t\treturn errors.Errorf(\"lock is already acquired on file %s\", f.filePath)\n\t}\n\tif err != nil {\n\t\tpanic(fmt.Sprintf(\"Error acquiring lock on file %s: %s\", f.filePath, err))\n\t}\n\n\t// only mutate the lock db reference AFTER validating that the lock was held.\n\tf.db = db\n\n\treturn nil\n}\n\n// Determine if the lock is currently held open.\nfunc (f *FileLock) IsLocked() bool {\n\treturn f.db != nil\n}\n\n// Unlock releases a previously acquired lock. We achieve this by closing\n// the previously opened db. FileUnlock can be called multiple times.\nfunc (f *FileLock) Unlock() {\n\tif f.db == nil {\n\t\treturn","sourceCodeStart":201,"sourceCodeEnd":237,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/common/ledger/util/leveldbhelper/leveldb_helper.go#L201-L237","documentation":"This panic occurs in the file-lock wrapper around LevelDB when opening the lock file fails with an error other than syscall.EAGAIN. The library only treats EAGAIN as a normal 'lock already held' condition; any other open failure is considered unrecoverable and panics. It signals that the lock database could not be opened at all, so locking state is unknown.","triggerScenarios":"Calling FileLock.Lock() on a path whose underlying leveldb.OpenFile fails with something other than EAGAIN — e.g. corrupted lock DB files, permission denied on the directory, disk I/O error, or the directory being unusable. As seen in TestFileLock/TestFileLockLockUnlockLock, normal usage opens the file in an existing empty dir; failure of OpenFile with an unexpected error triggers the panic.","commonSituations":"Running a peer/node whose data directory has wrong permissions or is read-only; leftover/corrupted LOCK files from an unclean shutdown; disk full; the lock path pointing at a file where a directory is expected; running two processes on NFS where flock semantics differ and OpenFile fails unexpectedly.","solutions":["Check filesystem permissions and ownership on the lock file's directory and ensure the process user can read/write it","Inspect/clear corrupted LevelDB lock artifacts (back up first), or point the lock path at a clean directory","Check disk space (df -h) and I/O health in logs (dmesg) for underlying OpenFile failures","Ensure only one process uses the path and the filesystem supports proper locking (avoid NFS for lock/ledger dirs)"],"exampleFix":"// before (panics on unexpected open error)\nif err != nil {\n\tpanic(fmt.Sprintf(\"Error acquiring lock on file %s: %s\", f.filePath, err))\n}\n// after (ensure a clean, writable directory before Lock)\nif err := os.MkdirAll(filepath.Dir(lockPath), 0755); err != nil { return err }\nif err := os.Chmod(filepath.Dir(lockPath), 0755); err != nil { return err }\nlock.Lock()","handlingStrategy":"try-catch","validationCode":"func canAcquireLock(dir string) error {\n\tfi, err := os.Stat(dir)\n\tif err != nil { return err }\n\tif !fi.IsDir() { return fmt.Errorf(\"%s is not a directory\", dir) }\n\ttmp := filepath.Join(dir, \".locktest\")\n\tf, err := os.Create(tmp); if err != nil { return err }\n\tf.Close(); os.Remove(tmp)\n\treturn nil\n}","typeGuard":"func isOpenError(err error) bool { return err != nil && errors.Is(err, syscall.EAGAIN) }","tryCatchPattern":"func() {\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tlog.Errorf(\"lock acquisition panicked: %v\", r)\n\t\t\t// check permissions/disk/corruption, then retry on a clean path\n\t\t}\n\t}()\n\tlock.Lock()\n}()","preventionTips":["Ensure the lock directory exists and is writable by the process user before locking","Never place ledger/lock directories on NFS; use local filesystems","Check disk space and clean unclean-shutdown artifacts before startup","Run only one process per lock path"],"tags":["leveldb","filesystem","locking","panic"],"backgroundTag":"file-lock-acquisition-failed","analyzedSha":"2736b63f8fd5932511d56fe68b7039d15977f7f6","analyzedAt":"2026-09-04T08:52:36.465Z","contentChangedAt":"2026-09-04T08:52:36.465Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}