abhigyanpatwari/GitNexus · error · StorageDeletionError

err.message

Error message

err.message

What it means

This is a 400 response from the DELETE repository endpoint. Before deleting a repo's index storage, the server calls requireDeletableStoragePath, which throws StorageDeletionError when the path is not a registered, deletable GitNexus storage directory. The server refuses to delete and returns the validator's message to the client.

Solutions

  1. Check the error message body for the specific path problem and verify the repo entry's storagePath exists and looks like a GitNexus index directory
  2. Confirm GITNEXUS_STORAGE_PATH / GITNEXUS_STORAGE_ROOT env vars match where the index was actually created
  3. Run `gitnexus status` (or re-run `gitnexus analyze`) to re-register the storage directory, then retry the delete
  4. If the path is safe but unregistered, remove it manually with rm after confirming it contains no data you need

Example fix

// before: deleting a repo whose storage dir was moved
await fetch(`/api/repos/${name}`, { method: 'DELETE' });
// 400 { error: 'Unsafe index storage path ...' }

// after: re-register storage first
cd my-repo && gitnexus analyze --index-only
await fetch(`/api/repos/${name}`, { method: 'DELETE' }); // 200
Defensive patterns

Strategy: try-catch

Validate before calling

const repo = await (await fetch('/api/repos')).json().then(rs => rs.find(r => r.name === name));
if (!repo || !repo.storagePath || !repo.sourceAvailable) throw new Error('no registered storage to delete');

Try / catch

const res = await fetch(`/api/repos/${name}`, { method: 'DELETE' });
if (res.status === 400) {
  const { error } = await res.json();
  console.error(`Deletion refused: ${error} — verify the repo's storage path is a registered GitNexus index`);
}

Prevention

When it happens

Trigger: Calling DELETE /api/repos/:name (the repo deletion route) when the repo's .gitnexus storage path fails the deletability check — e.g. the resolved storage path is outside the configured storage root, the entry has no registered storage path, or the directory does not look like a GitNexus index.

Common situations: Developers hitting this usually deleted or moved the repo's .gitnexus directory manually, pointed GITNEXUS_STORAGE_PATH/GITNEXUS_STORAGE_ROOT at a non-standard location, or try to remove a repo whose storage was created by an older GitNexus version with an unrecognized layout.

Understand the failure class

Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.

Related errors


AI-assisted analysis of abhigyanpatwari/GitNexus@ac9a4e9abd (2026-09-15). Data as JSON: /api/errors/83b75ab0b0a5660f. Report an issue: GitHub.

Appendix: source

Thrown at gitnexus/src/server/api.ts:1225

  // delete; tighten if abuse is observed.
  app.delete('/api/repo', createRouteLimiter(), requireTrustedOrigin, async (req, res) => {
    try {
      const repoName = requestedRepo(req);
      if (!repoName) {
        res.status(400).json({ error: 'Missing repo name' });
        return;
      }
      const entry = await resolveRepo(repoName, false, undefined, { validateStorage: false });
      if (!entry) {
        res.status(404).json({ error: 'Repository not found' });
        return;
      }
      let storagePath: string;
      try {
        storagePath = await requireDeletableStoragePath(entry);
      } catch (err: any) {
        if (err instanceof StorageDeletionError) {
          res.status(400).json({ error: err.message });
          return;
        }
        res.status(400).json({ error: err.message || 'Unsafe index storage path' });
        return;
      }

      // Acquire repo lock — prevents deleting while analyze/embed is in flight
      const lockKey = storagePath;
      const lockErr = acquireRepoLock(lockKey);
      if (lockErr) {
        res.status(409).json({ error: lockErr });
        return;
      }

      try {
        // Close any open LadybugDB handle before deleting files
        try {
          await closeLbug();

View on GitHub (pinned to ac9a4e9abd)