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
- 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
- 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
- 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)
- Use `lsof <surviving-file>` (Linux/macOS) or Resource Monitor (Windows) to identify which process holds the handle, then kill it
- 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
- Always stop `gitnexus serve` and MCP processes before running `gitnexus analyze`
- On Windows, add an antivirus exclusion for the .gitnexus/ directory
- Use a pre-analyze script that checks for running GitNexus processes
- In CI, ensure only one analyze job runs per repo at a time
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
- Cannot start dirty-state recovery — the interrupted run's La
- GitNexus could not move the LadybugDB WAL sidecar at ${dbPat
- LadybugDB WAL corruption detected for ${repoId}. Run `gitnex
- LadybugDB not found at ${dbPath}. Run: gitnexus analyze
- LadybugDB checkpoint sidecar is present but unreachable for
AI-assisted analysis of abhigyanpatwari/GitNexus@d540b00184 (2026-08-12).
Data as JSON: /api/errors/40d038b4bcf83c7e.
Report an issue: GitHub.