Devolutions/UniGetUI · error · InvalidDataException
PyPI simple index returned 0 packages — response may be malf
Error message
PyPI simple index returned 0 packages — response may be malformed
What it means
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.
Source
Thrown at src/UniGetUI.PackageEngine.Managers.Pip/Pip.cs:234
catch (Exception e)
{
logger.Error($"Could not write PyPI index file cache to {cacheFile}: {e.Message}");
}
return names;
}
internal static string[] ParseSimpleIndexProjectNames(string json)
{
var projects = (JsonNode.Parse(json) as JsonObject)?["projects"] as JsonArray;
string[] names = projects?
.Select(p => p?["name"]?.GetValue<string>())
.Where(n => !string.IsNullOrEmpty(n))
.Select(n => n!)
.ToArray() ?? [];
if (names.Length == 0)
throw new InvalidDataException("PyPI simple index returned 0 packages — response may be malformed");
return names;
}
internal static string[] SelectSearchMatches(string query, IEnumerable<string> allNames)
{
string queryLower = query.ToLowerInvariant();
return allNames
.Where(n => n.Contains(queryLower, StringComparison.OrdinalIgnoreCase))
.OrderBy(n => n.StartsWith(queryLower, StringComparison.OrdinalIgnoreCase) ? 0 : 1)
.ThenBy(n => n.Length)
.Take(MaxSearchResults)
.ToArray();
}
private static async Task<string?> FetchLatestVersionAsync(string packageName)
{
await _versionFetchSemaphore.WaitAsync().ConfigureAwait(false);View on GitHub (pinned to 9b1d7d0eab)
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.
Example fix
// before
if (names.Length == 0)
throw new InvalidDataException("PyPI simple index returned 0 packages — response may be malformed");
// after: keep last good cache
if (names.Length == 0)
{
Logger.Warn("PyPI index parse yielded 0 packages; keeping previous cache");
return _cachedNames ?? throw new InvalidDataException("...");
} Defensive patterns
Strategy: fallback
Validate before calling
// Validate the parsed JSON shape before trusting it.
var node = JsonNode.Parse(json);
var projects = (node as JsonObject)?["projects"] as JsonArray;
if (projects is null || projects.Count == 0)
return _cachedNames ?? throw new InvalidDataException("PyPI index empty/malformed"); Type guard
static bool LooksLikeValidIndex(JsonNode? n) =>
(n as JsonObject)?["projects"] is JsonArray arr && arr.Count > 0; Try / catch
try { names = Pip.ParseSimpleIndexProjectNames(json); }
catch (InvalidDataException ex)
{
Logger.Warn($"PyPI index malformed; keeping prior cache. {ex.Message}");
names = _cachedNames ?? throw;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: Truncated/corrupt download; intermediary proxy returning an HTML error page parsed as empty projects; crates.io-style schema drift.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- PyPI simple index returned {(int)response.StatusCode} {respo
- Failed to parse brew info JSON
- Microsoft Store python alias is not a valid python install
AI-assisted analysis of Devolutions/UniGetUI@9b1d7d0eab (2026-08-13).
Data as JSON: /api/errors/4330e6f50ad01280.
Report an issue: GitHub.