abhigyanpatwari/GitNexus · error · AnalysisNotFinalizedError

Analysis not finalized: missing registry-entry for

Error message

Analysis not finalized: missing registry-entry for ${resolved} at ${storagePath} (registry: ${getGlobalRegistryPath()})

What it means

AnalysisNotFinalizedError with kind 'registry-entry' means the metadata file exists at storagePath but readRegistryStrict found no registry entry pairing this repository path with this storagePath. Analysis is only considered finalized when both the on-disk metadata and a matching strict-read registry row exist.

Solutions

  1. Re-run `gitnexus analyze` with the same storage configuration so registration completes and the registry row is written.
  2. Fix the registry file to include an entry pairing the repo path with this storagePath (or remove the orphan metadata and re-analyze).
  3. Align GITNEXUS_STORAGE_PATH / storagePath option between the analyze and consuming commands.
  4. Check the registry path in the message — you may be consulting a different registry than the one populated during analyze.

Example fix

// before
await ensureFinalized(repo, { storagePath: "/external/slot" }); // no registry row
// after
await registerRepo(repo, { storagePath: "/external/slot" });
await ensureFinalized(repo, { storagePath: "/external/slot" });
Defensive patterns

Strategy: validation

Validate before calling

const entries = JSON.parse(await readFile(registryPath, "utf8"));
const ok = entries.some((e) =>
  path.resolve(e.path) === path.resolve(repoPath) &&
  path.resolve(e.storagePath) === path.resolve(storagePath));
if (!ok) await registerRepo(repoPath, { storagePath });

Try / catch

try {
  await readIndex(repoPath, { storagePath });
} catch (err) {
  if (err instanceof AnalysisNotFinalizedError && err.kind === "registry-entry") {
    await registerRepo(repoPath, { storagePath }); // write the missing row
    await readIndex(repoPath, { storagePath });
  } else throw err;
}

Prevention

When it happens

Trigger: A finalize/read path finds meta at storagePath, but no entry in the global registry has entry.path == repo and entry.storagePath == storagePath (canonicalized comparison) — the metadata is present while the registry row is missing or points elsewhere.

Common situations: The registry file was manually edited, truncated, or restored from an older backup losing the row; the repo was analyzed with default storage but is now read with an explicit storagePath; registration failed midway after metadata write; environment (GITNEXUS_STORAGE_PATH) differs between analyze and read.

Understand the failure class

Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.

Related errors


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

Appendix: source

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

      ? getStoragePaths(resolved).storagePath
      : validateConfiguredStoragePath(resolvedStoragePath);
  const metaPath = path.join(storagePath, INDEX_METADATA_FILE);

  try {
    await fs.access(metaPath);
  } catch {
    throw new AnalysisNotFinalizedError(resolved, storagePath, 'meta', getGlobalRegistryPath());
  }

  const canonicalRepoPath = canonicalizePath(resolved);
  const canonicalStoragePath = canonicalizePath(storagePath);
  const registeredAtStoragePath = (await readRegistryStrict()).some(
    (entry) =>
      registryPathEquals(canonicalizePath(entry.path), canonicalRepoPath) &&
      registryPathEquals(canonicalizePath(entry.storagePath), canonicalStoragePath),
  );
  if (!registeredAtStoragePath) {
    throw new AnalysisNotFinalizedError(
      resolved,
      storagePath,
      'registry-entry',
      getGlobalRegistryPath(),
    );
  }
};

/**
 * Thrown by {@link assertSafeStoragePath} when {@link requireDeletableStoragePath}
 * rejects the registry entry. Repository-local `.gitnexus` may still be
 * removed when it is missing, empty, unowned, or foreign; an external slot
 * is removable only when metadata binds both the repository and that exact
 * path. CLI destructive commands (`remove`, `clean --all`) should catch this
 * and exit non-zero without deleting anything.
 */
export class UnsafeStoragePathError extends Error {
  readonly kind = 'UnsafeStoragePathError' as const;

View on GitHub (pinned to ac9a4e9abd)