{"record":{"id":"c17c291a6b01a194","repo":"abhigyanpatwari/GitNexus","slug":"refusing-to-delete-storage-the-storage-inspection-state-is","errorCode":null,"errorMessage":"Refusing to delete storage: the storage inspection state is \"${inspection.state}\"","messagePattern":"Refusing to delete storage: the storage inspection state is \"(.+?)\"","errorType":"exception","errorClass":"StorageDeletionError","httpStatus":null,"severity":"error","filePath":"gitnexus/src/storage/storage-resolver.ts","lineNumber":717,"sourceCode":"    isPathAncestor(comparableStorage, comparableRepo)\n  ) {\n    throw new StorageDeletionError(\n      expectedStoragePath,\n      actualStoragePath,\n      undefined,\n      'the target is the repository, one of its parents, or a filesystem root',\n    );\n  }\n\n  const inspection = await inspectRegisteredStorage({\n    path: repoPath,\n    storagePath: actualStoragePath,\n  });\n  const allowedStates: readonly StorageState[] = storageIsLocal\n    ? ['missing', 'empty', 'unowned', 'owned', 'foreign']\n    : ['owned'];\n  if (!allowedStates.includes(inspection.state)) {\n    throw new StorageDeletionError(\n      expectedStoragePath,\n      actualStoragePath,\n      inspection,\n      `the storage inspection state is \"${inspection.state}\"`,\n    );\n  }\n  return actualStoragePath;\n};\n\n/** Ensure a selected index directory is usable before an analysis takes its lock. */\nexport const ensureStoragePathWritable = async (storagePath: string): Promise<void> => {\n  const resolved = validateConfiguredStoragePath(storagePath);\n  await fsp.mkdir(resolved, { recursive: true });\n  const stat = await fsp.stat(resolved);\n  if (!stat.isDirectory()) {\n    throw new InvalidStoragePathError(`Index storage path is not a directory: ${resolved}`);\n  }\n  await fsp.access(resolved, fs.constants.R_OK | fs.constants.W_OK);","sourceCodeStart":699,"sourceCodeEnd":735,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/ac9a4e9abd8fd3058c070b72c23402a4f887929a/gitnexus/src/storage/storage-resolver.ts#L699-L735","documentation":"A StorageDeletionError thrown when the pre-deletion inspection yields a state outside the allowed set: for a locally-owned .gitnexus the allowed states are missing/empty/unowned/owned/foreign, while an external storage slot may only be deleted when its state is 'owned'. Any other state (e.g. an external slot that is 'foreign' or 'unowned' relative to this repo) aborts the delete.","triggerScenarios":"Deleting a repo whose storage is an external slot (GITNEXUS_STORAGE_PATH-style) that inspection classifies as not owned by this repository; deleting through a storage path pointing at another repo's index.","commonSituations":"Two checkouts sharing one external index directory and the second one attempting cleanup; a moved repository whose external slot is registered under the original repoPath; typos in GITNEXUS_STORAGE_PATH selecting a foreign slot.","solutions":["Delete the storage from the repository that actually owns it (run the delete where the slot is 'owned').","Unset GITNEXUS_STORAGE_PATH to use the repo's own .gitnexus, whose states are all deletable.","Re-register or re-point the external slot at the correct repoPath, then retry.","Manually remove the foreign external slot directory after confirming ownership outside the tool."],"exampleFix":"// before (external slot owned by another checkout)\nGITNEXUS_STORAGE_PATH=/data/slots/repo-a npx gitnexus clean repo-b\n// after (use repo-b's own storage)\nunset GITNEXUS_STORAGE_PATH && npx gitnexus clean repo-b","handlingStrategy":"try-catch","validationCode":"const inspection = await inspectStoragePath(storagePath);\nconst isLocal = isRepositoryLocalStoragePath(repoPath, storagePath);\nconst allowed = isLocal ? ['missing','empty','unowned','owned','foreign'] : ['owned'];\nif (!allowed.includes(inspection.state)) {\n  console.error(`External slot state is ${inspection.state}; delete from the owning repo instead`);\n}","typeGuard":"const isDeletable = (i: StorageInspection, isLocal: boolean): boolean =>\n  isLocal ? true : i.state === 'owned';","tryCatchPattern":"try {\n  await deleteRepoStorage(repoName);\n} catch (err) {\n  if (err instanceof StorageDeletionError && err.inspection) {\n    console.error(`Storage state ${err.inspection.state} not deletable here; run from the owning checkout`);\n  } else throw err;\n}","preventionTips":["Perform deletions from the checkout that owns the external storage slot.","Prefer the default per-repo .gitnexus storage to avoid cross-checkout ownership issues.","Verify slot ownership (state === 'owned') before external-slot cleanup.","After moving a repo, re-register it so slot ownership matches the new path."],"tags":["storage","deletion","ownership","safety-guard"],"backgroundTag":"permission-denied","analyzedSha":"ac9a4e9abd8fd3058c070b72c23402a4f887929a","analyzedAt":"2026-09-15T23:29:44.066Z","contentChangedAt":"2026-09-15T23:29:44.066Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}