{"record":{"id":"d593c0ffe88d1c66","repo":"abhigyanpatwari/GitNexus","slug":"index-lock-verification-failed-lockpath","errorCode":null,"errorMessage":"Index lock verification failed: ${lockPath}","messagePattern":"Index lock verification failed: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"gitnexus/src/storage/index-lock.ts","lineNumber":632,"sourceCode":"            } else {\n              holder = current;\n            }\n          } else if (code === 'EPERM') {\n            throw err;\n          } else if (isLockUnwritableCode(code)) {\n            return deniedCreateHandle(lockPath, me, err);\n          } else {\n            throw err;\n          }\n        }\n        if (createdMain && fd !== undefined) {\n          try {\n            writeSync(fd, JSON.stringify(me));\n          } finally {\n            closeSync(fd);\n          }\n          if (readRecord(lockPath)?.token !== me.token) {\n            throw new Error(`Index lock verification failed: ${lockPath}`);\n          }\n          let released = false;\n          return {\n            record: me,\n            release: () => {\n              if (released) return;\n              released = true;\n              // No async boundary: a cooperating contender cannot replace a live\n              // owner's record before this one-shot unlink. Unknown is not ours.\n              try {\n                if (readRecord(lockPath)?.token !== me.token) return;\n                unlinkSync(lockPath);\n              } catch {\n                /* best-effort on exit; never retry against a successor */\n              }\n            },\n          };\n        }","sourceCodeStart":614,"sourceCodeEnd":650,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/ac9a4e9abd8fd3058c070b72c23402a4f887929a/gitnexus/src/storage/index-lock.ts#L614-L650","documentation":"After creating analyze.lock via O_EXCL and writing its JSON ownership record, acquireViaFile re-reads the file and asserts the record still carries this attempt's token. If it does not, the write did not durably land or something replaced the file between write and read — the lock would be unverifiable ownership, so the acquisition is refused (the error is then wrapped/rolled back by the caller's cleanup paths). This is a self-check invariant, not a contention signal.","triggerScenarios":"writeSync to the freshly created lock fd succeeded but a subsequent readRecord(lockPath) returns a record whose token differs (file truncated, replaced, or written concurrently by another writer), or readRecord returns null because the content is malformed/empty.","commonSituations":"Unreliable filesystem semantics (NFS, some FUSE/network mounts) where O_EXCL create+write isn't atomic across clients; two processes sharing a mount in separate network namespaces bypassing the socket lock (the documented cross-netns caveat); disk-full or I/O errors silently corrupting the record; external tools touching .gitnexus/ files.","solutions":["Retry the acquisition — a transient I/O blip usually clears on the next attempt.","Move the index to a reliable local filesystem; avoid NFS/FUSE mounts for .gitnexus/ (or set GITNEXUS_STORAGE_PATH to a local dir).","If you share a bind-mounted index across containers, set GITNEXUS_INDEX_LOCK_BACKEND=file and ensure all writers share the same mount and mount namespace semantics.","Stop external processes (sync clients, editors, security scanners) from touching files under .gitnexus/.","Check dmesg/filesystem health for underlying I/O errors; run analyze again after a clean state (no writers, per RUNBOOK.md)."],"exampleFix":"// before (index on NFS)\n// repo on ~/net/repo, .gitnexus/ served over NFS\n\n// after (local storage path)\n$ GITNEXUS_STORAGE_PATH=~/.local/share/gitnexus/repo npx gitnexus analyze","handlingStrategy":"fallback","validationCode":"// Prefer reliable local storage; detect risky mounts up front\nimport { statfsSync } from 'node:fs';\nconst fsType = statfsSync('.gitnexus').type; // e.g. 'nfs', 'fuse.*'\nif (/^(nfs|cifs|fuse)/.test(String(fsType))) {\n  console.warn('index on a network/FUSE filesystem — use GITNEXUS_STORAGE_PATH on a local disk');\n}","typeGuard":null,"tryCatchPattern":"try {\n  const handle = await acquireIndexLock(lockDir);\n} catch (err) {\n  if (err instanceof Error && err.message.startsWith('Index lock verification failed')) {\n    // unverified ownership — retry once, then fail closed (never write without the lock)\n    await sleep(1000);\n    return acquireIndexLock(lockDir);\n  }\n  throw err;\n}","preventionTips":["Place the index on a local disk; avoid NFS/FUSE for .gitnexus/.","Cross-container shared mounts: set GITNEXUS_INDEX_LOCK_BACKEND=file on every writer and share one mount namespace story.","Keep external tools (editors, scanners, sync clients) out of .gitnexus/.","Retry transient verification failures, but never proceed lock-free."],"tags":["filesystem","locking","invariant","io"],"backgroundTag":"internal-invariant-violation","analyzedSha":"ac9a4e9abd8fd3058c070b72c23402a4f887929a","analyzedAt":"2026-09-15T23:29:44.066Z","contentChangedAt":"2026-09-15T23:29:44.066Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}