{"record":{"id":"4330e6f50ad01280","repo":"Devolutions/UniGetUI","slug":"pypi-simple-index-returned-0-packages-response-m","errorCode":null,"errorMessage":"PyPI simple index returned 0 packages — response may be malformed","messagePattern":"PyPI simple index returned 0 packages — response may be malformed","errorType":"exception","errorClass":"InvalidDataException","httpStatus":null,"severity":"error","filePath":"src/UniGetUI.PackageEngine.Managers.Pip/Pip.cs","lineNumber":234,"sourceCode":"            catch (Exception e)\n            {\n                logger.Error($\"Could not write PyPI index file cache to {cacheFile}: {e.Message}\");\n            }\n\n            return names;\n        }\n\n        internal static string[] ParseSimpleIndexProjectNames(string json)\n        {\n            var projects = (JsonNode.Parse(json) as JsonObject)?[\"projects\"] as JsonArray;\n            string[] names = projects?\n                .Select(p => p?[\"name\"]?.GetValue<string>())\n                .Where(n => !string.IsNullOrEmpty(n))\n                .Select(n => n!)\n                .ToArray() ?? [];\n\n            if (names.Length == 0)\n                throw new InvalidDataException(\"PyPI simple index returned 0 packages — response may be malformed\");\n\n            return names;\n        }\n\n        internal static string[] SelectSearchMatches(string query, IEnumerable<string> allNames)\n        {\n            string queryLower = query.ToLowerInvariant();\n            return allNames\n                .Where(n => n.Contains(queryLower, StringComparison.OrdinalIgnoreCase))\n                .OrderBy(n => n.StartsWith(queryLower, StringComparison.OrdinalIgnoreCase) ? 0 : 1)\n                .ThenBy(n => n.Length)\n                .Take(MaxSearchResults)\n                .ToArray();\n        }\n\n        private static async Task<string?> FetchLatestVersionAsync(string packageName)\n        {\n            await _versionFetchSemaphore.WaitAsync().ConfigureAwait(false);","sourceCodeStart":216,"sourceCodeEnd":252,"githubUrl":"https://github.com/Devolutions/UniGetUI/blob/9b1d7d0eab91620fc15c86b36676532095936ae3/src/UniGetUI.PackageEngine.Managers.Pip/Pip.cs#L216-L252","documentation":"ParseSimpleIndexProjectNames throws InvalidDataException when the parsed 'projects' array yields zero package names. The JSON was valid and contained a 'projects' node, but every entry lacked a usable 'name' — a signal the PyPI response is malformed or the schema changed.","triggerScenarios":"PyPI returns a JSON whose 'projects' array is empty, or whose elements all have null/empty 'name' fields; a schema change in the v1+json simple index.","commonSituations":"Truncated/corrupt download; intermediary proxy returning an HTML error page parsed as empty projects; crates.io-style schema drift.","solutions":["Delete the cached index file and re-download to rule out a corrupt local copy.","Log the raw JSON length/preview when this fires to detect schema drift.","Retry once; if it persists, report 'PyPI search temporarily unavailable' rather than caching an empty list."],"exampleFix":"// before\nif (names.Length == 0)\n    throw new InvalidDataException(\"PyPI simple index returned 0 packages — response may be malformed\");\n// after: keep last good cache\nif (names.Length == 0)\n{\n    Logger.Warn(\"PyPI index parse yielded 0 packages; keeping previous cache\");\n    return _cachedNames ?? throw new InvalidDataException(\"...\");\n}","handlingStrategy":"fallback","validationCode":"// Validate the parsed JSON shape before trusting it.\nvar node = JsonNode.Parse(json);\nvar projects = (node as JsonObject)?[\"projects\"] as JsonArray;\nif (projects is null || projects.Count == 0)\n    return _cachedNames ?? throw new InvalidDataException(\"PyPI index empty/malformed\");","typeGuard":"static bool LooksLikeValidIndex(JsonNode? n) =>\n    (n as JsonObject)?[\"projects\"] is JsonArray arr && arr.Count > 0;","tryCatchPattern":"try { names = Pip.ParseSimpleIndexProjectNames(json); }\ncatch (InvalidDataException ex)\n{\n    Logger.Warn($\"PyPI index malformed; keeping prior cache. {ex.Message}\");\n    names = _cachedNames ?? throw;\n}","preventionTips":["Keep the last good cache and reuse it on a malformed response.","Log raw JSON length/preview on failure to detect schema drift or proxy HTML.","Delete a corrupt cache file to force a clean re-download."],"tags":["pip","pypi","json-parse","data-integrity","malformed-response"],"backgroundTag":null,"analyzedSha":"9b1d7d0eab91620fc15c86b36676532095936ae3","analyzedAt":"2026-08-13T12:19:18.278Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}