abhigyanpatwari/GitNexus · error · Error
${message}
Error message
${message} What it means
An `IndexLockHandle` acquired for read-only or shared use reports `lockFree === true`, meaning the caller does not actually own the exclusive index lock. `requireExclusiveIndexLock` is a guard for mutating operations: it throws the supplied message rather than letting a writer proceed against an index it does not have exclusive access to, which would corrupt the storage.
Solutions
- Acquire the index lock in exclusive (write) mode before the mutating operation and pass that handle to the writer.
- Ensure the lock is still held (not yet released) at the call site; reorder release to after the write completes.
- Audit the handle's origin — if only a shared handle is available, either upgrade the acquisition or make the operation read-only.
- Use a try/finally pattern so the exclusive handle is released exactly once, after `requireExclusiveIndexLock` and the write succeed or fail.
Example fix
// before: read-only handle handed to a writer
const handle = await acquireSharedIndexLock(dir);
requireExclusiveIndexLock(handle, 'write requires exclusive lock'); // throws: lockFree
// after
const handle = await acquireExclusiveIndexLock(dir);
try {
requireExclusiveIndexLock(handle, 'write requires exclusive lock');
await writeToIndex(dir);
} finally {
handle.release();
} Defensive patterns
Strategy: type-guard
Validate before calling
// verify ownership before any write
if (!handle || handle.lockFree) {
throw new Error('Attempt to write with a non-exclusive or released index lock handle');
} Type guard
const holdsExclusiveLock = (handle) => handle != null && handle.lockFree === false;
Try / catch
try {
requireExclusiveIndexLock(handle, 'write requires an exclusive index lock');
await writeToIndex(dir);
} catch (e) {
// reacquire the lock in exclusive mode and retry once
const fresh = await acquireExclusiveIndexLock(dir);
try { await writeToIndex(dir); } finally { fresh.release(); }
} Prevention
- Pair every index write with an exclusive-mode acquisition in the same function.
- Release locks in try/finally so a handle is never used after release.
- Type-sign write helpers to accept only the exclusive handle type, not the shared one.
- Keep lock acquisition and release in one scope to avoid stale-handle reuse after refactors.
When it happens
Trigger: Code calls `requireExclusiveIndexLock(handle, msg)` with a handle obtained from a non-exclusive (shared/read-only) lock acquisition, or a handle whose lock has already been released (`lockFree` true), before performing an index write/mutation such as analyze writeback or compaction.
Common situations: A code path acquired the lock with the shared/read-only API but then reached a write routine; the lock was released early (early return / error path) and the stale handle was still passed on; refactoring changed which handle flows into a writer without updating the acquisition mode.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- GitNexus: unable to acquire init lock after
- Index lock acquisition timed out
- Index lock verification failed
- timeout
- Analysis already in progress
AI-assisted analysis of abhigyanpatwari/GitNexus@ac9a4e9abd (2026-09-15).
Data as JSON: /api/errors/3dc660583efbe207.
Report an issue: GitHub.
Appendix: source
Thrown at gitnexus/src/storage/index-lock.ts:185
/** Present only for acquisition/reclaim guard contention, requiring quiesced recovery. */
readonly guardPath?: string;
constructor(holder: LockRecord, waitedMs: number, holderKnown = true, guardPath?: string) {
super(formatIndexLockTimeoutMessage(holder, waitedMs, holderKnown, guardPath));
this.name = 'IndexLockTimeoutError';
this.holder = holder;
this.holderKnown = holderKnown;
this.guardPath = guardPath;
}
}
export const isIndexLockGuardTimeout = (
error: unknown,
): error is IndexLockTimeoutError & { guardPath: string } =>
error instanceof IndexLockTimeoutError && error.guardPath !== undefined;
/** Writers must refuse a handle that does not own the lock. */
export const requireExclusiveIndexLock = (handle: IndexLockHandle, message: string): void => {
if (handle.lockFree) throw new Error(message);
};
const formatIndexLockTimeoutMessage = (
holder: LockRecord,
waitedMs: number,
holderKnown: boolean,
guardPath?: string,
): string => {
if (guardPath !== undefined) {
return (
`Timed out after ${waitedMs}ms waiting for acquisition/reclaim guard ${guardPath}. ` +
`Quiesce all relevant writers and prevent restart before manual recovery. ` +
`Never remove the guard while writers may run; see RUNBOOK.md for quiesced recovery.`
);
}
if (holderKnown) {
return (
`Timed out after ${waitedMs}ms waiting for another gitnexus analyze ` +View on GitHub (pinned to ac9a4e9abd)