{"record":{"id":"22702251ee8b2532","repo":"abhigyanpatwari/GitNexus","slug":"registry-redirect-missing-location","errorCode":null,"errorMessage":"Registry redirect missing location","messagePattern":"Registry redirect missing location","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"gitnexus/src/core/update-check.ts","lineNumber":189,"sourceCode":"  parsed.password = '';\n  validateGitUrl(parsed.toString());\n  return parsed;\n}\n\nasync function fetchLatest(packageUrl: string): Promise<string> {\n  let url = sanitizedHttpUrl(packageUrl);\n  for (let redirects = 0; ; redirects += 1) {\n    const response = await fetch(url.toString(), {\n      method: 'GET',\n      redirect: 'manual',\n      headers: { accept: 'application/json' },\n      signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),\n    });\n    if (response.status >= 300 && response.status < 400) {\n      if (redirects >= MAX_REDIRECTS) throw new Error('Too many registry redirects');\n      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;","sourceCodeStart":171,"sourceCodeEnd":207,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/0d1aed942f0e8b5d3bac27519fff441aceea722d/gitnexus/src/core/update-check.ts#L171-L207","documentation":"When the registry responds with a 3xx status, fetchLatest reads the Location header to continue; if the 3xx response carries no Location header it cannot proceed and throws this error. A 3xx without Location is malformed per HTTP semantics, so it is treated as a broken response rather than silently stopping.","triggerScenarios":"A proxy/load-balancer or captive portal returns 301/302/307 responses without a Location header; some anti-bot appliances emit bare 302s to intercept clients.","commonSituations":"Corporate filtering appliances between GitNexus and the registry; a half-broken reverse proxy in front of a private registry; a registry URL pointed at an HTML page served by a misbehaving server.","solutions":["Inspect the raw response (curl -i <registry-url>) to identify the device sending bare 3xxs","Remove or correctly configure the intercepting proxy in front of the registry host","Point the update registry URL at a host that answers directly with 200 JSON"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try { const latest = await fetchLatest(url, 0); } catch (e) { if (e.message === 'Registry redirect missing location') { console.warn('Malformed 3xx from registry (no Location header); skipping update check'); } else throw e; }","preventionTips":["Remove intercepting proxies/firewalls that emit bare 3xx responses","Verify with curl -i that the registry host answers sanely before configuring it","Point update checks at hosts that return direct 200 JSON"],"tags":["npm","registry","http","redirect"],"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"}