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

  1. Acquire the index lock in exclusive (write) mode before the mutating operation and pass that handle to the writer.
  2. Ensure the lock is still held (not yet released) at the call site; reorder release to after the write completes.
  3. Audit the handle's origin — if only a shared handle is available, either upgrade the acquisition or make the operation read-only.
  4. 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

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


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)