abhigyanpatwari/GitNexus · error
Cannot verify acquisition/reclaim guard ownership
Error message
Cannot verify acquisition/reclaim guard ownership: ${guardPath}. Acquisition refused; see RUNBOOK.md for quiesced recovery. What it means
releaseAcquisitionGuard verifies, before an index-lock handle is returned, that the acquisition/reclaim guard file at guardPath is owned by this attempt (its record carries this attempt's token). Here the guard exists and is readable, but its token differs from this attempt's token — another attempt or a leftover record owns the name — so the function throws unverifiedGuardError and refuses the acquisition fail-closed, pointing the operator to RUNBOOK.md for quiesced recovery. This prevents releasing or claiming a lock on behalf of the wrong owner.
Solutions
- Quiesce all GitNexus processes touching the repo (as RUNBOOK.md describes), remove the stale guard file, and retry the acquisition.
- Identify the other live holder from the guard record (pid/hostname/invocationId) and wait for it to finish before retrying.
- On shared/network filesystems, move the repo to a local filesystem with proper O_EXCL semantics to avoid cross-process record clobbering.
- If the guard is a confirmed orphan (holder pid is dead), delete guardPath and lockPath manually, then re-run the command.
Defensive patterns
Strategy: try-catch
Validate before calling
import fs from 'node:fs';
// before acquiring, ensure no acquisition guard is already present on this repo
const guardPath = lockGuardPath(lockPath);
if (fs.existsSync(guardPath)) {
console.warn('Stale/live acquisition guard present — quiesce other processes first (RUNBOOK.md)');
} Try / catch
let handle;
try {
handle = await acquireIndexLock(/* ... */);
} catch (err) {
if (err instanceof Error && err.message.includes('Cannot verify acquisition/reclaim guard ownership')) {
// fail-closed: quiesce other processes, clean the stale guard, then retry
console.error('Quiesce all gitnexus processes and follow RUNBOOK.md quiesced recovery');
} else throw err;
} Prevention
- Never run overlapping gitnexus commands against the same repo; one acquisition at a time.
- Do not manually edit or delete files under the lock directory while commands run.
- Use a local filesystem (not NFS) for repos so O_EXCL guard semantics hold.
- Follow RUNBOOK.md's documented recovery instead of ad-hoc cleanup when acquisition fails.
- Clean up crashed processes promptly so their guards are not reclaimed by later attempts.
When it happens
Trigger: During acquireViaFile's guard cleanup: the guard file exists, readRecord(guardPath) succeeds, but the parsed record's token does not equal this attempt's token — i.e. this attempt's metadata write failed or the record was overwritten by another attempt before cleanup ran.
Common situations: Two processes racing to acquire the index lock where the second overwrote the guard record; a previous crashed acquisition whose guard was reclaimed by another attempt; an NFS/network filesystem without coherent O_EXCL semantics causing record cross-write.
Related errors
- Index lock acquisition timed out
- LadybugDB unavailable for
- lockErr
- Analysis already in progress
- Analysis already in progress
AI-assisted analysis of abhigyanpatwari/GitNexus@ac9a4e9abd (2026-09-15).
Data as JSON: /api/errors/087039dd2f786140.
Report an issue: GitHub.
Appendix: source
Thrown at gitnexus/src/storage/index-lock.ts:354
const remembered = args.lastLiveHolder ?? unknownHolder();
throw new IndexLockTimeoutError(
remembered,
args.now - args.startedAt,
args.lastLiveHolder !== null,
);
};
/** Drop this attempt's guard. A throw here discards the pending handle. */
const releaseAcquisitionGuard = (
guardPath: string,
me: LockRecord,
createdMain: boolean,
lockPath: string,
): void => {
try {
const guardRecord = readRecord(guardPath);
if (guardRecord && guardRecord.token !== me.token) {
throw unverifiedGuardError(guardPath);
}
if (guardRecord?.token === me.token) {
// Must complete before returning a workload handle or polling.
unlinkSync(guardPath);
return;
}
try {
lstatSync(guardPath);
} catch (err) {
if ((err as NodeJS.ErrnoException).code === 'ENOENT') {
throw unverifiedGuardError(guardPath);
}
throw err;
}
// Exists but unreadable: this process created the name via O_EXCL.
// Drop it so a failed metadata write cannot leave a permanent orphan,
// then refuse this attempt.
unlinkSync(guardPath);View on GitHub (pinned to ac9a4e9abd)