{"record":{"id":"56d2cda559f9234a","repo":"abhigyanpatwari/GitNexus","slug":"gitnexus-unable-to-acquire-init-lock-after-init","errorCode":null,"errorMessage":"GitNexus: unable to acquire init lock after ${INIT_LOCK_MAX_ATTEMPTS} attempts — another gitnexus process may be initializing the same database (${lockPath})","messagePattern":"GitNexus: unable to acquire init lock after (.+?) attempts — another gitnexus process may be initializing the same database \\((.+?)\\)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"gitnexus/src/core/lbug/lbug-adapter.ts","lineNumber":438,"sourceCode":"            logger.warn(\n              `GitNexus: failed to release init lock (${code ?? 'UNKNOWN'}): ${summarizeError(err)}`,\n            );\n          }\n        }\n      };\n    } catch (err) {\n      if ((err as NodeJS.ErrnoException)?.code !== 'EEXIST') {\n        throw err; // Unexpected error — propagate immediately\n      }\n\n      // Lock file exists — check if it's stale\n      const broken = await tryBreakStaleLock(lockPath);\n      if (broken && attempt < INIT_LOCK_MAX_ATTEMPTS) {\n        continue; // Stale lock removed — retry immediately\n      }\n\n      if (attempt === INIT_LOCK_MAX_ATTEMPTS) {\n        throw new Error(\n          `GitNexus: unable to acquire init lock after ${INIT_LOCK_MAX_ATTEMPTS} attempts — ` +\n            `another gitnexus process may be initializing the same database (${lockPath})`,\n        );\n      }\n\n      // Live process holds the lock — wait and retry\n      await new Promise((resolve) => setTimeout(resolve, INIT_LOCK_RETRY_DELAY_MS));\n    }\n  }\n\n  // Unreachable — loop always throws or returns\n  throw new Error('GitNexus: init lock acquisition failed unexpectedly');\n};\n\n/** Exported for testing — returns the lock file path for a given dbPath. */\nexport const _initLockPathForTest = initLockPath;\n\nconst runWithSessionLock = async <T>(operation: () => Promise<T>): Promise<T> => {","sourceCodeStart":420,"sourceCodeEnd":456,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/d540b00184d71a896261ee02670da9a92d59d8f7/gitnexus/src/core/lbug/lbug-adapter.ts#L420-L456","documentation":"Thrown by `acquireInitLock` after `INIT_LOCK_MAX_ATTEMPTS` (6) failed attempts to create the exclusive init lock file via `O_CREAT | O_EXCL`. Between attempts it tries to break a stale lock and waits `INIT_LOCK_RETRY_DELAY_MS` (500ms) when a live process holds it. The lock serializes cross-process DB initialization; if a real concurrent gitnexus process is initializing the same database, the lock legitimately stays held and this error names the lock path so the operator can identify the contention.","triggerScenarios":"Two gitnexus processes (e.g. `gitnexus analyze` and `gitnexus serve`, or an MCP server) initializing the same database concurrently such that the lock file remains held for the full ~3s retry window; or a live (non-stale) lock that `tryBreakStaleLock` correctly refuses to remove.","commonSituations":"An editor MCP server and a CLI analyze hitting the same repo at once; a cron job overlapping a manual run; a previous process that crashed but left a lock the staleness check still considers live (e.g. its PID is reused).","solutions":["Stop the other concurrent gitnexus process (serve/MCP/another analyze) using this repository, then retry.","If no other process is running, inspect the lock file at the reported `lockPath` — its JSON payload names a PID; verify that PID is genuinely dead and remove the lock file if so.","Wait a few seconds and retry — the 6x500ms budget may simply have been too short for a legitimately-finishing concurrent init.","Avoid running `analyze` and `serve` against the same repo simultaneously."],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":"function isInitLockError(err): boolean {\n  return /unable to acquire init lock after \\d+ attempts/i.test(\n    err instanceof Error ? err.message : String(err),\n  );\n}","tryCatchPattern":"// The lock holder usually finishes quickly; retry once after a short delay,\n// after confirming no concurrent process is intentionally running.\nfor (let attempt = 0; attempt < 2; attempt++) {\n  try {\n    await initLbug(dbPath);\n    break;\n  } catch (err) {\n    if (/unable to acquire init lock/i.test(err.message) && attempt === 0) {\n      await new Promise(r => setTimeout(r, 1000));\n      continue;\n    }\n    throw err;\n  }\n}","preventionTips":["Do not run analyze and serve/MCP against the same repo at the same time.","In cron, use a process-level lock or stagger schedules to avoid overlap.","If a lock is stale (holder PID dead), gitnexus auto-breaks it; only manually remove it if the PID is confirmed dead."],"tags":["lock","init","concurrency","database","cross-process"],"backgroundTag":null,"analyzedSha":"d540b00184d71a896261ee02670da9a92d59d8f7","analyzedAt":"2026-08-12T19:50:25.132Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}