abhigyanpatwari/GitNexus · error · Error
Refusing to delete ${dbPath}: resolved path ${realPath} is o
Error message
Refusing to delete ${dbPath}: resolved path ${realPath} is outside storage directory What it means
A safety guard in `ensureLbugInitialized`: when the existing `dbPath` is a directory (an old-style directory database or leftover) the code resolves the real path via `realpath` and refuses to recursively delete it unless the resolved path is inside the storage (parent) directory. This prevents a symlink or bind-mount that points the db path OUTSIDE the storage directory from causing `fs.rm({recursive:true})` to wipe an unrelated directory tree. Symlinks themselves are just unlinked (never followed).
Source
Thrown at gitnexus/src/core/lbug/lbug-adapter.ts:832
conn = usable.conn;
currentDbReadOnly = true;
} else {
// LadybugDB stores the database as a single file (not a directory).
// If the path already exists, it must be a valid LadybugDB database file.
// Remove stale empty directories or files from older versions.
try {
const stat = await fs.lstat(dbPath);
if (stat.isSymbolicLink()) {
// Never follow symlinks — just remove the link itself
await fs.unlink(dbPath);
} else if (stat.isDirectory()) {
// Verify path is within expected storage directory before deleting
const realPath = await fs.realpath(dbPath);
const parentDir = path.dirname(dbPath);
const realParent = await fs.realpath(parentDir);
const safePrefix = realParent.endsWith(path.sep) ? realParent : realParent + path.sep;
if (!realPath.startsWith(safePrefix) && realPath !== realParent) {
throw new Error(
`Refusing to delete ${dbPath}: resolved path ${realPath} is outside storage directory`,
);
}
// Old-style directory database or empty leftover - remove it
await fs.rm(dbPath, { recursive: true, force: true });
}
// If it's a file, assume it's an existing LadybugDB database - LadybugDB will open it
} catch (err) {
if (!isMissingFileError(err)) {
throw err;
}
// Path doesn't exist, which is what LadybugDB wants for a new database
}
// -------------------------------------------------------------------------
// Cross-process critical section: acquire init lock, clean orphan sidecars,
// and open the database. The lock prevents a TOCTOU race where another
// process could create a fresh DB between our access() check and theView on GitHub (pinned to d540b00184)
Solutions
- Remove the symlink/bind-mount so `dbPath` resolves inside the storage directory, then retry.
- Manually delete the old directory database at its real location if it is genuinely stale, then let gitnexus create a fresh file-based DB.
- Do not point the dbPath at a location outside the `.gitnexus/` storage directory.
Defensive patterns
Strategy: validation
Validate before calling
import { realpathSync, lstatSync } from 'node:fs';
import path from 'node:path';
function assertDbPathInStorage(dbPath, storageDir) {
const st = lstatSync(dbPath);
if (st.isDirectory()) {
const real = realpathSync(dbPath);
const realParent = realpathSync(path.dirname(dbPath));
const safePrefix = realParent.endsWith(path.sep) ? realParent : realParent + path.sep;
if (!real.startsWith(safePrefix) && real !== realParent) {
throw new Error(`dbPath resolves outside storage dir; refusing to let gitnexus delete it.`);
}
}
}
assertDbPathInStorage(dbPath, storageDir); Try / catch
try {
await initLbug(dbPath);
} catch (err) {
if (/resolved path .* is outside storage directory/i.test(err.message)) {
// A symlink/bind-mount points the db outside .gitnexus — remove it and retry.
console.error(err.message);
process.exit(9);
}
throw err;
} Prevention
- Never symlink or bind-mount the dbPath to a location outside the .gitnexus storage directory.
- If relocating storage, move the whole storage directory rather than symlinking the db file.
- Keep the .gitnexus directory self-contained on one filesystem.
When it happens
Trigger: `dbPath` exists, is a directory, and `fs.realpath(dbPath)` resolves to a location whose `realPath` is neither inside `realParent` nor equal to it — e.g. a symlink-to-directory or bind-mount pointing outside `.gitnexus/`. The guard throws rather than delete.
Common situations: A user symlinked the database path to another volume/dir; a bind-mount or container volume maps the db path outside the storage tree; an old directory-style database that was relocated.
Related errors
- candidate destination must be a regular non-symlink file: {r
- candidate overlay cannot traverse symlinks: {overlay}
- evidence source must be a regular non-symlink file: {path}
- results directory must not traverse symlinks: {root}
- results artifact parent must be a real directory: {current}
AI-assisted analysis of abhigyanpatwari/GitNexus@d540b00184 (2026-08-12).
Data as JSON: /api/errors/69a6c617b4f6353c.
Report an issue: GitHub.