abhigyanpatwari/GitNexus · error

GitNexus: unable to acquire init lock after

Error message

GitNexus: unable to acquire init lock after ${INIT_LOCK_MAX_ATTEMPTS} attempts — another gitnexus process may be initializing the same database (${lockPath})

What it means

The init lock in lbug-adapter.ts serializes database initialization with an exclusive lock file: up to INIT_LOCK_MAX_ATTEMPTS (6) attempts spaced INIT_LOCK_RETRY_DELAY_MS (500ms) apart, breaking stale locks between attempts. If a live process still holds the lock after ~3 seconds, this error reports that another GitNexus process is probably initializing the same database, naming the lock path.

Solutions

  1. Wait for the in-flight process to finish initializing, then re-run the command
  2. Stop the concurrent process (gitnexus serve / MCP server) that holds the lock, then retry
  3. If you can verify no other GitNexus process is running, remove the lock file at the path named in the message
  4. Serialize writers: ensure only one process initializes a given database at a time (one analyze, or one serve, per repo/storage dir)

Example fix

# before: two processes race
$ gitnexus serve & gitnexus analyze .
# after: stop the server first, then initialize
$ gitnexus mcp stop   # or kill the serve process
gitnexus analyze .
gitnexus serve &
Defensive patterns

Strategy: retry

Validate before calling

import fs from 'node:fs';

const lockPath = getLockPath(dbPath); // same path the adapter names in the error
try {
  fs.accessSync(lockPath);
  console.error(`Init lock present at ${lockPath} — another gitnexus process may be initializing; wait or stop it.`);
  process.exit(1);
} catch {
  /* no lock — safe to proceed */
}
await openDatabase(dbPath);

Try / catch

try {
  await withLbugDb(dbPath, run);
} catch (err) {
  if (err instanceof Error && err.message.includes('unable to acquire init lock')) {
    // wait for the concurrent initializer to finish (or stop it), verify no gitnexus process runs,
    // optionally delete the lock file named in the message, then retry once
  }
  throw err;
}

Prevention

When it happens

Trigger: Running `gitnexus analyze` (or any DB-opening command) concurrently with a `gitnexus serve`/MCP process that is initializing the same index; two CLI invocations racing on a shared GITNEXUS_HOME storage dir; parallel CI jobs sharing a cache volume; or a leftover lock file that tryBreakStaleLock did not consider stale.

Common situations: IDE MCP server auto-starting while the user runs the CLI on the same repo; CI matrix jobs sharing an index volume; a previous crash leaving a lock whose PID/heuristics still look live.

Related errors


AI-assisted analysis of abhigyanpatwari/GitNexus@ac9a4e9abd (2026-08-20). Data as JSON: /api/errors/56d2cda559f9234a. Report an issue: GitHub.

Appendix: source

Thrown at gitnexus/src/core/lbug/lbug-adapter.ts:431

            logger.warn(
              `GitNexus: failed to release init lock (${code ?? 'UNKNOWN'}): ${summarizeError(err)}`,
            );
          }
        }
      };
    } catch (err) {
      if ((err as NodeJS.ErrnoException)?.code !== 'EEXIST') {
        throw err; // Unexpected error — propagate immediately
      }

      // Lock file exists — check if it's stale
      const broken = await tryBreakStaleLock(lockPath);
      if (broken && attempt < INIT_LOCK_MAX_ATTEMPTS) {
        continue; // Stale lock removed — retry immediately
      }

      if (attempt === INIT_LOCK_MAX_ATTEMPTS) {
        throw new Error(
          `GitNexus: unable to acquire init lock after ${INIT_LOCK_MAX_ATTEMPTS} attempts — ` +
            `another gitnexus process may be initializing the same database (${lockPath})`,
        );
      }

      // Live process holds the lock — wait and retry
      await new Promise((resolve) => setTimeout(resolve, INIT_LOCK_RETRY_DELAY_MS));
    }
  }

  // Unreachable — loop always throws or returns
  throw new Error('GitNexus: init lock acquisition failed unexpectedly');
};

/** Exported for testing — returns the lock file path for a given dbPath. */
export const _initLockPathForTest = initLockPath;

const runWithSessionLock = async <T>(operation: () => Promise<T>): Promise<T> => {

View on GitHub (pinned to ac9a4e9abd)