abhigyanpatwari/GitNexus · error · UnsafeStoragePathError

Refusing to remove storage path for safety: expected "${expe

Error message

Refusing to remove storage path for safety: expected "${expectedStoragePath}" under the repo's .gitnexus subfolder, but the registry entry has "${actualStoragePath}". This usually means the registry entry is corrupted or was hand-edited. Delete the entry manually from ~/.gitnexus/registry.json and re-run analyze.

What it means

Destructive commands (gitnexus remove, clean --all) delete the entry's storagePath from disk. As a final safety gate, assertSafeStoragePath requires storagePath to be exactly <entry.path>/.gitnexus (Windows case-insensitive, POSIX case-sensitive). Any other value — a hand-edited or corrupted registry entry — aborts deletion so an arbitrary directory can never be recursively removed.

Source

Thrown at gitnexus/src/storage/repo-manager.ts:1282

 *     the registry field. But `clean --all` DOES iterate the registry
 *     and trust each entry's stored storagePath (same shape as
 *     `remove`), so this helper must be wired into that loop too.
 *   - `server/api.ts` recomputes storagePath from `getStoragePath(entry.path)`
 *     and so is likewise safe-by-construction.
 *
 * Pure string check — does NOT require the paths to exist on disk.
 * Windows: case-insensitive; POSIX: case-sensitive. Matches the
 * comparison shape used elsewhere in this module.
 */
export const assertSafeStoragePath = (entry: RegistryEntry): void => {
  const expected = path.join(path.resolve(entry.path), '.gitnexus');
  const actual = path.resolve(entry.storagePath);
  const matches =
    process.platform === 'win32'
      ? expected.toLowerCase() === actual.toLowerCase()
      : expected === actual;
  if (!matches) {
    throw new UnsafeStoragePathError(entry, expected, actual);
  }
};

/**
 * Resolve a user-supplied target string (from `gitnexus remove <target>`
 * or equivalent MCP tool argument) to a single registry entry.
 *
 * Match precedence (first hit wins, subsequent tiers are only tried if
 * the prior tier produces zero matches):
 *   1. Exact resolved-path match (Windows: case-insensitive).
 *      Paths are unique by registry construction, so a path match can
 *      never be ambiguous.
 *   2. Exact `name` match (case-insensitive). If ≥ 2 entries share the
 *      name — only possible via `--allow-duplicate-name` (#829) —
 *      throws {@link RegistryAmbiguousTargetError}.
 *
 * No fuzzy / partial matching — unambiguous, scriptable behaviour is
 * more important than convenience for destructive commands.

View on GitHub (pinned to aac7515d2a)

Solutions

  1. Open ~/.gitnexus/registry.json and fix the entry (set storagePath to <entry.path>/.gitnexus), or delete the bogus entry manually as the message instructs, then re-run
  2. Re-register cleanly: remove the broken entry, run gitnexus analyze on the repo again
  3. Never point storagePath at shared/external directories — this guard exists precisely to keep deletion inside the repo's .gitnexus

Example fix

// ~/.gitnexus/registry.json — before
{ "name": "app", "path": "/srv/app", "storagePath": "/srv" }

// after
{ "name": "app", "path": "/srv/app", "storagePath": "/srv/app/.gitnexus" }
Defensive patterns

Strategy: validation

Validate before calling

import path from 'node:path';
function isSafeStorageEntry(entry: { path: string; storagePath: string }): boolean {
  const expected = path.join(path.resolve(entry.path), '.gitnexus');
  const actual = path.resolve(entry.storagePath);
  return process.platform === 'win32'
    ? expected.toLowerCase() === actual.toLowerCase()
    : expected === actual;
}

Prevention

When it happens

Trigger: registry.json was edited by hand so storagePath points somewhere else (or entry.path changed without storagePath); a mangled/migrated registry; then running gitnexus remove or clean --all.

Common situations: Users hand-editing ~/.gitnexus/registry.json to 'fix' paths; backup/sync tools rewriting home files; registry formats from incompatible versions.

Related errors


AI-assisted analysis of abhigyanpatwari/GitNexus@aac7515d2a (2026-08-20). Data as JSON: /api/errors/4da9c0e2834edd38. Report an issue: GitHub.