{"record":{"id":"6045c74d2b4d75c6","repo":"abhigyanpatwari/GitNexus","slug":"registry-latest-version-is-invalid","errorCode":null,"errorMessage":"Registry latest version is invalid","messagePattern":"Registry latest version is invalid","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"gitnexus/src/core/update-check.ts","lineNumber":205,"sourceCode":"      const location = response.headers.get('location');\n      await response.body?.cancel().catch(() => {});\n      if (!location) throw new Error('Registry redirect missing location');\n      url = sanitizedHttpUrl(location, url.toString());\n      continue;\n    }\n    if (!response.ok) {\n      await response.body?.cancel().catch(() => {});\n      throw new Error(`Registry returned ${response.status}`);\n    }\n    const parsed = JSON.parse(await readResponseBody(response)) as {\n      version?: unknown;\n      'dist-tags'?: { latest?: unknown };\n    };\n    const latest =\n      (typeof parsed.version === 'string' ? parsed.version : undefined) ??\n      (typeof parsed['dist-tags']?.latest === 'string' ? parsed['dist-tags'].latest : undefined);\n    if (typeof latest !== 'string' || !STRICT_UPDATE_VERSION.test(latest)) {\n      throw new Error('Registry latest version is invalid');\n    }\n    return latest;\n  }\n}\n\nasync function publishMonotonically(\n  entry: UpdateCacheEntry,\n  attemptStartedAt: number,\n): Promise<void> {\n  const current = await readCache(entry.registry);\n  const currentAt = current ? Date.parse(current.lastCheckAt) : Number.NaN;\n  // A later in-the-past write wins. Future-dated entries (wall-clock) are\n  // clock-skew poison and must stay replaceable so a later holder can repair them.\n  if (Number.isFinite(currentAt) && currentAt <= Date.now() && currentAt > attemptStartedAt) {\n    return;\n  }\n  await fs.mkdir(getGlobalDir(), { recursive: true });\n  await writeFileAtomic(cacheFile(), `${JSON.stringify(entry)}\\n`, 1);","sourceCodeStart":187,"sourceCodeEnd":223,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/0d1aed942f0e8b5d3bac27519fff441aceea722d/gitnexus/src/core/update-check.ts#L187-L223","documentation":"After fetching and JSON-parsing the registry response, fetchLatest extracts the latest version from either parsed.version or parsed['dist-tags'].latest and validates it against STRICT_UPDATE_VERSION. If neither field is a string matching the strict semver pattern, it throws this error — the response was HTTP-valid JSON but did not carry a usable version.","triggerScenarios":"The endpoint returns JSON without a version/dist-tags.latest field (e.g. an error object like {\"error\":\"not found\"}, an empty object, or a HTML page that happens to parse); or the version string is malformed (pre-release tags the strict regex rejects, 'latest' as a literal, empty string).","commonSituations":"A custom mirror serving a different response schema than npm's; registry returning an error JSON with HTTP 200 (some proxies do); pointing the update URL at a generic endpoint instead of the package version document.","solutions":["curl the endpoint and confirm the JSON contains version or dist-tags.latest with a valid semver string","Point the update registry/package URL at the correct version/dist-tags document for the package","Fix or replace the mirror so it serves npm-shaped version JSON"],"exampleFix":"// before (mirror returns {\"ok\":true})\n// throws: Registry latest version is invalid\n// after — correct endpoint returns:\n// {\"name\":\"gitnexus\",\"version\":\"1.14.0\",\"dist-tags\":{\"latest\":\"1.14.0\"}}","handlingStrategy":"type-guard","validationCode":"const doc = await (await fetch(registryUrl)).json(); const ok = typeof doc?.version === 'string' && /^\\d+\\.\\d+\\.\\d+/.test(doc.version) || typeof doc?.['dist-tags']?.latest === 'string'; if (!ok) skipUpdateCheck();","typeGuard":"function isValidVersionDoc(d: unknown): d is { version?: string; 'dist-tags'?: { latest?: string } } { const o = d as Record<string, unknown>; const v = o?.version ?? o?.['dist-tags']?.['latest']; return typeof v === 'string' && STRICT_UPDATE_VERSION.test(v); }","tryCatchPattern":"try { const latest = await fetchLatest(url, 0); } catch (e) { if (e.message === 'Registry latest version is invalid') { console.warn('Registry served no valid version field; skipping update check'); } else throw e; }","preventionTips":["Only point update checks at endpoints returning npm-shaped version JSON","Validate custom mirror response schemas against npm's before deploying","Compare the endpoint's JSON output against the package on the canonical registry during setup"],"tags":["npm","registry","versioning","response-validation"],"backgroundTag":"unexpected-response-shape","analyzedSha":"0d1aed942f0e8b5d3bac27519fff441aceea722d","analyzedAt":"2026-09-08T00:40:44.970Z","contentChangedAt":"2026-09-08T00:40:44.970Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}