{"record":{"id":"02d1720a49af54f6","repo":"Devolutions/UniGetUI","slug":"pypi-simple-index-returned-int-response-statusco","errorCode":null,"errorMessage":"PyPI simple index returned {(int)response.StatusCode} {response.ReasonPhrase}","messagePattern":"PyPI simple index returned (.+?) (.+?)","errorType":"http","errorClass":"HttpRequestException","httpStatus":null,"severity":"error","filePath":"src/UniGetUI.PackageEngine.Managers.Pip/Pip.cs","lineNumber":201,"sourceCode":"                string[] cached = File.ReadAllLines(cacheFile);\n                if (cached.Length > 0)\n                {\n                    lock (_cacheLock) { _cachedNames = cached; _cacheTimestamp = File.GetLastWriteTime(cacheFile); }\n                    return cached;\n                }\n                logger.Error(\"PyPI index file cache was empty, re-downloading...\");\n            }\n\n            // Download fresh index\n            logger.Log(\"Downloading PyPI simple index (one-time ~38 MB download, cached for 24 h)...\");\n            using HttpClient client = new(CoreTools.GenericHttpClientParameters);\n            client.DefaultRequestHeaders.UserAgent.ParseAdd(CoreData.UserAgentString);\n            client.DefaultRequestHeaders.Add(\"Accept\", \"application/vnd.pypi.simple.v1+json\");\n\n            using var request = new HttpRequestMessage(HttpMethod.Get, \"https://pypi.org/simple/\");\n            using HttpResponseMessage response = client.Send(request);\n            if (!response.IsSuccessStatusCode)\n                throw new HttpRequestException($\"PyPI simple index returned {(int)response.StatusCode} {response.ReasonPhrase}\");\n\n            string json = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();\n            string[] names = ParseSimpleIndexProjectNames(json);\n\n            logger.Log($\"Downloaded {names.Length} package names from PyPI\");\n\n            // Update memory cache before attempting file write so searches work even if file write fails\n            lock (_cacheLock) { _cachedNames = names; _cacheTimestamp = DateTime.Now; }\n\n            try\n            {\n                Directory.CreateDirectory(Path.GetDirectoryName(cacheFile)!);\n                File.WriteAllLines(cacheFile, names);\n            }\n            catch (Exception e)\n            {\n                logger.Error($\"Could not write PyPI index file cache to {cacheFile}: {e.Message}\");\n            }","sourceCodeStart":183,"sourceCodeEnd":219,"githubUrl":"https://github.com/Devolutions/UniGetUI/blob/9b1d7d0eab91620fc15c86b36676532095936ae3/src/UniGetUI.PackageEngine.Managers.Pip/Pip.cs#L183-L219","documentation":"Pip's index loader throws HttpRequestException when the GET to https://pypi.org/simple/ (the ~38 MB PyPI simple index, cached 24h) returns a non-success status. The status code and reason phrase are embedded in the message. This blocks pip package search until a successful download/cache.","triggerScenarios":"PyPI returns 4xx/5xx for the simple-index request: rate limiting (429), maintenance (5xx), DNS/proxy failure surfaced as a non-success, or a transient network blip.","commonSituations":"Corporate proxy/firewall blocking pypi.org; PyPI rate-limiting; transient outage; misconfigured proxy argument to pip.","solutions":["Retry after a short delay (the index is cached; transient failures clear on next refresh).","Verify network/proxy reachability to https://pypi.org/simple/ from the host.","If a stale cache file exists, the loader falls back to it only if non-empty; ensure pip proxy env (PIP_INDEX_URL/HTTP(S)_PROXY) is correct."],"exampleFix":"// before: single shot throws on non-2xx\nusing HttpResponseMessage response = client.Send(request);\nif (!response.IsSuccessStatusCode)\n    throw new HttpRequestException($\"PyPI simple index returned {(int)response.StatusCode} {response.ReasonPhrase}\");\n// after: one bounded retry\nfor (int i = 0; i < 2; i++)\n{\n    using var resp = client.Send(request.Clone());\n    if (resp.IsSuccessStatusCode) { /* parse */ return; }\n    if (i == 1) throw new HttpRequestException($\"PyPI simple index returned {(int)resp.StatusCode} {resp.ReasonPhrase}\");\n}","handlingStrategy":"retry","validationCode":"// Pre-flight reachability to pypi.org before the heavy download.\nusing var ping = await client.GetAsync(\"https://pypi.org/simple/\", HttpCompletionOption.ResponseHeadersRead);\nif (!ping.IsSuccessStatusCode) Logger.Warn($\"PyPI not reachable: {ping.StatusCode}\");","typeGuard":null,"tryCatchPattern":"int attempts = 0;\nHttpStatusCode? last = null;\nwhile (attempts < 3)\n{\n    try { /* download + parse index */ return; }\n    catch (HttpRequestException ex) { last = ParseStatus(ex.Message); attempts++; await Task.Delay(TimeSpan.FromSeconds(5 * attempts)); }\n}\nthrow new HttpRequestException($\"PyPI index unavailable after retries (last={last})\");","preventionTips":["Keep the 24h cache so transient PyPI failures don't break search immediately.","Ensure PIP_INDEX_URL/HTTP(S)_PROXY point at reachable PyPI mirrors.","Retry with backoff on 429/5xx rather than failing the first search."],"tags":["pip","pypi","network","http-request","rate-limit"],"backgroundTag":null,"analyzedSha":"9b1d7d0eab91620fc15c86b36676532095936ae3","analyzedAt":"2026-08-13T12:19:18.278Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}