abhigyanpatwari/GitNexus · error · LbugWipeError

Failed to remove the LadybugDB index files — still present a

Error message

Failed to remove the LadybugDB index files — still present after 5 attempts:
  - ${survivors}
The blocking handle may be another process, a lingering handle from this process's just-closed database, or an antivirus scan — an immediate re-run often succeeds. If it persists, stop any GitNexus MCP or serve process using this repository, add an antivirus exclusion for the GitNexus storage directory, then re-run the analyze.

What it means

Thrown as an LbugWipeError when wipeLbugDbFiles cannot delete all LadybugDB data files (the .db, .wal, .shadow family minus the contentless .lock) after exhausting HANDLE_RELEASE_PROBE_ATTEMPTS retries with linear back-off. The wipe runs during `gitnexus analyze` before recreating the index — if a file handle lingers (another process, antivirus scan, OS handle-release lag), the rm/access probe cycle fails every attempt and survivors remain. The error names the surviving file paths and advises on lock remediation.

Source

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

      if (!gone) survivors.push(f);
    }
    if (survivors.length === 0) return;
    if (attempt < HANDLE_RELEASE_PROBE_ATTEMPTS) {
      await sleep(HANDLE_RELEASE_PROBE_DELAY_MS * attempt);
    }
  }

  // Class split (FIX 2): the contentless `.lock` never fails the wipe.
  const dataSurvivors = survivors.filter((f) => f !== lockPath);
  if (survivors.includes(lockPath)) {
    logger.warn(
      `GitNexus: ${lockPath} is still present after the wipe retries — continuing: the ` +
        'lock file is contentless and initLbug recreates it; a genuinely held lock will ' +
        "surface as the reopen's own lock-busy error.",
    );
  }
  if (dataSurvivors.length > 0) {
    throw new LbugWipeError(dataSurvivors);
  }
};

export const isLbugReady = (): boolean => conn !== null && db !== null;

/**
 * Multi-label alternation over exactly the labels that can own embedding
 * rows: EMBEDDABLE_LABELS plus File, which embedding-pipeline.ts embeds as
 * the zero-symbol fallback for text-only repositories (#2454). Reserved
 * keywords are backtick-escaped via {@link escapeTableName}. Probed on
 * @ladybugdb/core 0.18.0 (this shipping review, FIX 4): the full multi-label
 * alternation parses, executes, and deletes exactly the joined rows —
 * replacing the unlabeled `MATCH (n)` that scanned EVERY node table per
 * chunk (BasicBlock-dominated under `--pdg`) when only embeddable labels
 * can match an embedding row. Including File is free for code repositories:
 * they never hold File embedding rows, so the extra label joins nothing.
 */
const embeddableLabelMatch = (): string =>

View on GitHub (pinned to d540b00184)

Solutions

  1. Stop all GitNexus processes (MCP server, serve, any analyze) using this repository, then re-run `gitnexus analyze` — an immediate re-run often succeeds once the handle clears
  2. On Windows, add an antivirus exclusion for the GitNexus storage directory (typically under .gitnexus/ in the repo root) to prevent Defender from briefly holding file handles
  3. If the surviving file is only the .lock, note that the wipe logic already classifies that as benign (continues with a warning) — this error only fires for data files (.db, .wal, .shadow)
  4. Use `lsof <surviving-file>` (Linux/macOS) or Resource Monitor (Windows) to identify which process holds the handle, then kill it
  5. As a last resort, manually `rm -rf` the surviving files from the .gitnexus/ storage directory, then re-run `gitnexus analyze`
Defensive patterns

Strategy: retry

Validate before calling

// Check if any process holds the DB files before wiping
import { access, constants } from 'fs/promises';
async function canWipe(lbugPath: string): Promise<boolean> {
  try {
    // Try opening for write — if another process holds it, this fails
    await access(lbugPath, constants.W_OK);
    return true;
  } catch {
    return false;
  }
}

Try / catch

try {
  await wipeLbugDbFiles(lbugPath);
} catch (e) {
  if (e instanceof LbugWipeError) {
    // Stop all GitNexus processes, add AV exclusion, then retry
    console.error(`Surviving files: ${e.survivors.join(', ')}`);
    console.error('Stop all gitnexus serve/MCP processes and re-run analyze.');
  }
  throw e;
}

Prevention

When it happens

Trigger: Running `gitnexus analyze` while a `gitnexus serve` or MCP process still holds the LadybugDB database open; Windows Defender or another antivirus scanner briefly opening the .wal/.db file for inspection during the wipe window; a non-ASCII path on Windows that causes intermittent fs.access failures; a lingering native Database handle from this same process that was closed in JS but whose OS-level file handle hasn't been released by libuv yet.

Common situations: Developer runs `gitnexus analyze` without first stopping a running `gitnexus serve` process that opened the DB read-only via the pool adapter; CI environment where parallel test processes share a storage directory; Windows development where Defender scans newly-created files; containerized environments with overlay filesystems that delay handle release; a crashed previous analyze that left zombie file handles.

Related errors


AI-assisted analysis of abhigyanpatwari/GitNexus@d540b00184 (2026-08-12). Data as JSON: /api/errors/40d038b4bcf83c7e. Report an issue: GitHub.