{"record":{"id":"4da9c0e2834edd38","repo":"abhigyanpatwari/GitNexus","slug":"refusing-to-remove-storage-path-for-safety-expect","errorCode":null,"errorMessage":"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.","messagePattern":"Refusing to remove storage path for safety: expected \"(.+?)\" under the repo's \\.gitnexus subfolder, but the registry entry has \"(.+?)\"\\. This usually means the registry entry is corrupted or was hand-edited\\. Delete the entry manually from ~/\\.gitnexus/registry\\.json and re-run analyze\\.","errorType":"exception","errorClass":"UnsafeStoragePathError","httpStatus":null,"severity":"error","filePath":"gitnexus/src/storage/repo-manager.ts","lineNumber":1282,"sourceCode":" *     the registry field. But `clean --all` DOES iterate the registry\n *     and trust each entry's stored storagePath (same shape as\n *     `remove`), so this helper must be wired into that loop too.\n *   - `server/api.ts` recomputes storagePath from `getStoragePath(entry.path)`\n *     and so is likewise safe-by-construction.\n *\n * Pure string check — does NOT require the paths to exist on disk.\n * Windows: case-insensitive; POSIX: case-sensitive. Matches the\n * comparison shape used elsewhere in this module.\n */\nexport const assertSafeStoragePath = (entry: RegistryEntry): void => {\n  const expected = path.join(path.resolve(entry.path), '.gitnexus');\n  const actual = path.resolve(entry.storagePath);\n  const matches =\n    process.platform === 'win32'\n      ? expected.toLowerCase() === actual.toLowerCase()\n      : expected === actual;\n  if (!matches) {\n    throw new UnsafeStoragePathError(entry, expected, actual);\n  }\n};\n\n/**\n * Resolve a user-supplied target string (from `gitnexus remove <target>`\n * or equivalent MCP tool argument) to a single registry entry.\n *\n * Match precedence (first hit wins, subsequent tiers are only tried if\n * the prior tier produces zero matches):\n *   1. Exact resolved-path match (Windows: case-insensitive).\n *      Paths are unique by registry construction, so a path match can\n *      never be ambiguous.\n *   2. Exact `name` match (case-insensitive). If ≥ 2 entries share the\n *      name — only possible via `--allow-duplicate-name` (#829) —\n *      throws {@link RegistryAmbiguousTargetError}.\n *\n * No fuzzy / partial matching — unambiguous, scriptable behaviour is\n * more important than convenience for destructive commands.","sourceCodeStart":1264,"sourceCodeEnd":1300,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/aac7515d2a8c50a1f8f923c6fb77218b333560d6/gitnexus/src/storage/repo-manager.ts#L1264-L1300","documentation":"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.","triggerScenarios":"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.","commonSituations":"Users hand-editing ~/.gitnexus/registry.json to 'fix' paths; backup/sync tools rewriting home files; registry formats from incompatible versions.","solutions":["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","Re-register cleanly: remove the broken entry, run gitnexus analyze on the repo again","Never point storagePath at shared/external directories — this guard exists precisely to keep deletion inside the repo's .gitnexus"],"exampleFix":"// ~/.gitnexus/registry.json — before\n{ \"name\": \"app\", \"path\": \"/srv/app\", \"storagePath\": \"/srv\" }\n\n// after\n{ \"name\": \"app\", \"path\": \"/srv/app\", \"storagePath\": \"/srv/app/.gitnexus\" }","handlingStrategy":"validation","validationCode":"import path from 'node:path';\nfunction isSafeStorageEntry(entry: { path: string; storagePath: string }): boolean {\n  const expected = path.join(path.resolve(entry.path), '.gitnexus');\n  const actual = path.resolve(entry.storagePath);\n  return process.platform === 'win32'\n    ? expected.toLowerCase() === actual.toLowerCase()\n    : expected === actual;\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never hand-edit storagePath in ~/.gitnexus/registry.json","Validate registry entries with this exact check before running destructive commands in automation","Re-register via gitnexus analyze instead of patching registry JSON"],"tags":["registry","safety","data-loss-prevention","configuration"],"backgroundTag":"registry-corruption","analyzedSha":"aac7515d2a8c50a1f8f923c6fb77218b333560d6","analyzedAt":"2026-08-20T23:29:22.980Z","schemaVersion":2},"datasetVersion":"2026-08-22T14:17:55.899Z"}