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
- Check permissions on the database directory and its lock file; make sure the running user can unlink files there.
- If warns recur, manually remove the leftover lock file while nothing else runs.
- Serialize gitnexus invocations against one DB (one analyze or one serve at a time) to shrink release/acquire races.
- 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
- Serialize initLbug callers per DB path so release/acquire windows never overlap.
- Ensure write+unlink permission on the DB directory for the service user.
- Add antivirus exclusions for lock/sidecar files on Windows to avoid EBUSY on unlink.
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
- ${source}: branch name must not contain whitespace.
- GitNexus could not move the LadybugDB WAL sidecar at ${dbPat
- Failed to remove the LadybugDB index files — still present a
- GitNexus: unable to inspect init lock (${code ?? 'UNKNOWN'})
- ${source}: branch name must not contain "..".
AI-assisted analysis of abhigyanpatwari/GitNexus@aac7515d2a (2026-08-20).
Data as JSON: /api/errors/50d70f334461e2e9.
Report an issue: GitHub.