abhigyanpatwari/GitNexus · error
LadybugDB not found at
Error message
LadybugDB not found at ${dbPath}. Run: gitnexus analyze What it means
doInitLbug (gitnexus/src/core/lbug/pool-adapter.ts:718) stat'd the database file before opening and it is not there: the pool has no index to serve. The pool is read-only by design — only `gitnexus analyze` creates the database, so the fix is to build the index first.
Solutions
- Run `gitnexus analyze` on the repository to build the index, then retry
- Verify the resolved dbPath actually exists on disk and fix the storage/repo mapping if it points elsewhere
- If the index was deliberately removed, restore it from backup or re-analyze
Example fix
// before — init throws when the index was never built
await initLbug(repoId, dbPath);
// after — build the index first, then open the pool
if (!(await fs.stat(dbPath).then(() => true, () => false))) {
await runAnalyze(repoPath); // creates the database
}
await initLbug(repoId, dbPath); Defensive patterns
Strategy: validation
Validate before calling
// check the index exists before opening the pool
import { stat } from 'node:fs/promises';
const exists = await stat(dbPath).then(
() => true,
() => false,
);
if (!exists) {
throw new Error(`no index at ${dbPath} — run gitnexus analyze first`);
} Type guard
const isLbugNotFound = (e: unknown): boolean =>
e instanceof Error && e.message.startsWith('LadybugDB not found at'); Try / catch
try {
await initLbug(repoId, dbPath);
} catch (e) {
if (isLbugNotFound(e)) {
// build the index, then retry the open
await runAnalyze(repoPath);
await initLbug(repoId, dbPath);
} else {
throw e;
}
} Prevention
- Make `gitnexus analyze` a provisioning step that runs before serve/MCP startup
- Pin the storage directory location in config so cleanup scripts cannot silently remove it
- Log the resolved dbPath at startup to catch mapping regressions early
When it happens
Trigger: Calling initLbug(repoId, dbPath) — directly or via MCP/serve startup — for a repository that was never analyzed, whose storage directory was deleted or moved, or whose repoId-to-path mapping resolves to the wrong location.
Common situations: Fresh clone with serve started before the first analyze; storage dir wiped by a cleanup script or CI; a config or mapping regression pointing at a non-existent path.
Related errors
- Batch execution failed for rows
- Connection pool integrity error: expected
- Failed to remove the LadybugDB index files — still present…
- FTS extension unavailable - cannot create FTS index
- FTS index ' ' on table exists but the LadybugDB FTS…
AI-assisted analysis of abhigyanpatwari/GitNexus@ac9a4e9abd (2026-09-15).
Data as JSON: /api/errors/6aebe54eb4e21743.
Report an issue: GitHub.
Appendix: source
Thrown at gitnexus/src/core/lbug/pool-adapter.ts:803
if (existing.checkedOut > 0) return { status: 'done', reopened: false };
// Awaited: see the rationale on the evictLRU call site in doInitLbug below.
await closeOne(repoId); // idle & changed → evict, then fall through to reopen the new file
}
return doInitLbug(repoId, dbPath);
};
/**
* Internal init — creates DB, pre-warms connections, loads FTS, then registers pool.
* Pool entry is registered LAST so concurrent executeQuery calls see either
* "not initialized" (and throw) or a fully ready pool — never a half-built one.
*/
async function doInitLbug(repoId: string, dbPath: string): Promise<InitLbugAttempt> {
// Check if database exists
try {
await fs.stat(dbPath);
} catch {
throw new Error(`LadybugDB not found at ${dbPath}. Run: gitnexus analyze`);
}
// Awaited: without this, the connection opened just below could race the
// checkpoint from the LRU victim's still-in-flight close (see evictLRU /
// closeOne). The caller holds withPoolLock for this attempt, so this await
// only covers this call's own evict-then-reopen — not other callers.
// Lock-retry re-enters this function after sleeping *outside* the mutex.
// evictLRU is a no-op unless the pool is full again (another repo may have
// taken the slot we freed on a prior attempt). Skipping it on retry would
// let a 6th native open race a still-resident victim's checkpoint.
await evictLRU();
// Reuse an existing native Database if another repoId already opened this path.
// This prevents buffer manager exhaustion from multiple mmap regions on the same file.
let shared = dbCache.get(dbPath);
if (shared && !shared.external && shared.dbIdentity) {
// #2614 F2: a cached read-only Database is keyed by dbPath and shared across
// pool consumers. If the on-disk index was rebuilt/swapped (new inode) whileView on GitHub (pinned to ac9a4e9abd)