abhigyanpatwari/GitNexus · warning

GitNexus: failed to release init lock (${code ?? 'UNKNOWN'})

Error message

GitNexus: failed to release init lock (${code ?? 'UNKNOWN'}): ${summarizeError(err)}

What it means

Emitted by the idempotent release function returned from the init-lock acquire: `fs.unlink(lockPath)` inside the release callback failed with something other than ENOENT. The error is swallowed after logging because release must never throw; a surviving lock file is handled later by the stale-lock breaker (PID-gone / INIT_LOCK_STALE_MS heuristics) on the next acquire.

Source

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

  await fs.mkdir(path.dirname(lockPath), { recursive: true });

  for (let attempt = 1; attempt <= INIT_LOCK_MAX_ATTEMPTS; attempt++) {
    try {
      const handle = await fs.open(
        lockPath,
        fsConstants.O_CREAT | fsConstants.O_EXCL | fsConstants.O_WRONLY,
      );
      await handle.writeFile(payload);
      await handle.close();

      // Return the idempotent release function
      return async () => {
        try {
          await fs.unlink(lockPath);
        } catch (err) {
          if (!isMissingFileError(err)) {
            const code = extractErrnoCode(err);
            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(

View on GitHub (pinned to aac7515d2a)

Solutions

  1. Check permissions on the database directory and its lock file; make sure the running user can unlink files there.
  2. If warns recur, manually remove the leftover lock file while nothing else runs.
  3. Serialize gitnexus invocations against one DB (one analyze or one serve at a time) to shrink release/acquire races.
  4. Add an antivirus exclusion for the .gitnexus directory on Windows.
Defensive patterns

Strategy: validation

Validate before calling

// Before releasing: only unlink a lock we still own (idempotent guard)
const release = await acquireInitLock(dbPath);
// ... work ...
await release(); // already swallows; to verify cleanliness afterwards:
import { access } from 'node:fs/promises';
const leftover = await access(lockPath).then(() => true, () => false);
if (leftover && noOtherGitnexusRunning()) await unlink(lockPath);

Prevention

When it happens

Trigger: initLbug completes and the returned release() is invoked, but unlink hits EPERM/EACCES (file owned by another user), EBUSY (Windows/antivirus handle), or the lock was already replaced by a different process's lock file. The warn records the residue; the next open may hit EEXIST and go through the stale-lock path.

Common situations: Multi-process overlap (serve + analyze finishing near-simultaneously); Windows Defender or indexers holding a handle on the lock; mixed-UID container volumes; read-only or quota-exhausted filesystems.

Related errors


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