{"record":{"id":"21bcf986c716ce0b","repo":"abhigyanpatwari/GitNexus","slug":"refusing-to-register-resolved-external-storage-metadata-must","errorCode":null,"errorMessage":"Refusing to register ${resolved}: external storage metadata must bind storagePath to the selected directory.","messagePattern":"Refusing to register (.+?): external storage metadata must bind storagePath to the selected directory\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"gitnexus/src/storage/repo-manager.ts","lineNumber":995,"sourceCode":"\n  // Production write paths pass the storage slot they already validated. Do\n  // not let a changed environment/registry redirect their registry entry, and\n  // require the metadata receipt to describe that same repository and slot.\n  // The omitted-option path deliberately retains legacy direct-call behavior.\n  if (opts?.storagePath !== undefined) {\n    if (!registryPathEquals(canonicalizePath(meta.repoPath), canonicalInput)) {\n      throw new Error(\n        `Refusing to register ${resolved}: metadata belongs to ${meta.repoPath}, not this repository.`,\n      );\n    }\n    if (\n      meta.storagePath === undefined &&\n      !registryPathEquals(\n        canonicalizePath(storagePath),\n        canonicalizePath(defaultStoragePath(resolved)),\n      )\n    ) {\n      throw new Error(\n        `Refusing to register ${resolved}: external storage metadata must bind storagePath to the selected directory.`,\n      );\n    }\n    if (\n      meta.storagePath !== undefined &&\n      !registryPathEquals(canonicalizePath(meta.storagePath), canonicalizePath(storagePath))\n    ) {\n      throw new Error(\n        `Refusing to register ${resolved}: metadata storagePath does not match the selected storage directory.`,\n      );\n    }\n  }\n\n  // Mutating writes must not treat an unreadable/truncated registry as empty\n  // (#3094): lenient `readRegistry()` returns `[]` on parse failure and would\n  // replace the machine-wide file with only this entry. ENOENT stays empty.\n  const entries = await readRegistryStrict();\n  const existingIdx = entries.findIndex((e) => {","sourceCodeStart":977,"sourceCodeEnd":1013,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/ac9a4e9abd8fd3058c070b72c23402a4f887929a/gitnexus/src/storage/repo-manager.ts#L977-L1013","documentation":"When an explicit (external) storagePath is selected, registerRepo requires the metadata receipt to bind storagePath to that directory. If meta.storagePath is absent and the storage directory is not the repo's default .gitnexus location, the write is refused — otherwise the registry would claim external storage while the metadata records none, breaking later lookups.","triggerScenarios":"Calling registerRepo with opts.storagePath set to a non-default directory while the metadata file in that directory has no storagePath field (e.g. metadata created by a default-location analyze).","commonSituations":"Analyze first ran with default storage (.gitnexus in-repo), then registration is attempted with GITNEXUS_STORAGE_PATH or an explicit storagePath pointing elsewhere; metadata was copied from a default-storage repo into an external slot.","solutions":["Re-run analysis so metadata is written into the selected external storage directory with storagePath recorded.","Point opts.storagePath at the directory the metadata actually names (or the repo's default .gitnexus).","Clear the stale metadata in the external directory and re-analyze.","Keep GITNEXUS_STORAGE_PATH consistent between the analyze and register invocations."],"exampleFix":"// before\nawait registerRepo(repo, { storagePath: \"/external/slot\" }); // meta.storagePath undefined\n// after\nawait analyze({ storagePath: \"/external/slot\" }); // regenerate meta there\nawait registerRepo(repo, { storagePath: \"/external/slot\" });","handlingStrategy":"validation","validationCode":"const meta = JSON.parse(await readFile(path.join(storagePath, \"index-meta.json\"), \"utf8\"));\nconst isDefault = path.resolve(storagePath) === path.resolve(repoPath, \".gitnexus\");\nif (!isDefault && (meta.storagePath === undefined || path.resolve(meta.storagePath) !== path.resolve(storagePath))) {\n  throw new Error(\"metadata not bound to selected external storage\");\n}","typeGuard":null,"tryCatchPattern":"try {\n  await registerRepo(repoPath, { storagePath });\n} catch (err) {\n  if (/external storage metadata must bind storagePath/.test(String(err))) {\n    await analyze({ repoPath, storagePath }); // regenerate metadata bound to the slot\n    await registerRepo(repoPath, { storagePath });\n  } else throw err;\n}","preventionTips":["Run analyze and register with identical storage options in the same environment.","Don't move metadata produced under default storage into external slots.","Use a wrapper script exporting the same GITNEXUS_STORAGE_PATH for all gitnexus invocations."],"tags":["registry","storage-path","metadata","registration"],"backgroundTag":"conflicting-config-options","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"}