beeradmoore/dlss-swapper · error · Exception
Could not deserialize manifest.json.
Error message
Could not deserialize manifest.json.
What it means
Thrown by DLLManager.UpdateManifestAsync when System.Text.Json deserialization of the downloaded manifest.json stream returns null. This indicates the stream did not contain a valid JSON object matching the Manifest schema (DeserializeAsync returns null only for empty input, so an empty or zero-length stream is the typical cause). The library treats a manifest it cannot parse as unrecoverable and stops updating.
Solutions
- Check the downloaded stream length before deserializing and fail earlier with a clearer download error
- Verify the manifest URL returns a valid manifest.json (curl it and inspect the body)
- Re-run the update after confirming network/proxy connectivity
- Pin to a known-good manifest source or bundled fallback manifest
Example fix
// before
var manifest = await JsonSerializer.DeserializeAsync(memoryStream, SourceGenerationContext.Default.Manifest);
if (manifest is null)
{
throw new Exception("Could not deserialize manifest.json.");
}
// after
if (memoryStream.Length == 0)
{
throw new Exception($"Manifest download was empty ({manifestUrl}).");
}
var manifest = await JsonSerializer.DeserializeAsync(memoryStream, SourceGenerationContext.Default.Manifest);
if (manifest is null)
{
throw new Exception($"Could not deserialize manifest.json from {manifestUrl} (empty body).");
} Defensive patterns
Strategy: try-catch
Validate before calling
// before calling UpdateManifestAsync
using var head = await httpClient.GetAsync(manifestUrl, HttpCompletionOption.ResponseHeadersRead);
if (!head.IsSuccessStatusCode || head.Content.Headers.ContentLength == 0)
throw new InvalidOperationException($"Manifest at {manifestUrl} is empty or unreachable."); Type guard
static bool HasManifestContent(Stream s) => s.CanSeek && s.Length > 0;
Try / catch
try
{
await dllManager.UpdateManifestAsync();
}
catch (Exception ex) when (ex.Message.Contains("Could not deserialize manifest.json"))
{
logger.LogWarning(ex, "Manifest download/parse failed; keeping existing manifest.");
} Prevention
- Check HTTP status and ContentLength before parsing the manifest stream
- Keep a last-known-good manifest as fallback
- Retry transient download failures with backoff
- Log the first bytes of the response when parsing fails
When it happens
Trigger: Calling UpdateManifestAsync when the remote manifest download produced an empty stream (failed/redirected download writing 0 bytes), or the response body is whitespace/empty rather than Manifest-shaped JSON.
Common situations: CDN or proxy returning an empty 200 response; manifest URL changed and now serves an empty body; network middlebox stripping the body; offline/captive portal returning an empty stub page.
Understand the failure class
Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.
Related errors
- Could not deserialize GOGCatalogResponse for url
- Could not deserialize GOGEmbedFilteredResponse for url
- Could not load GitHub release data.
- Unknown AssetType
- LocalRecord was null when attempting to extract dll from…
AI-assisted analysis of beeradmoore/dlss-swapper@ab9b1e2d4b (2026-09-15).
Data as JSON: /api/errors/431763dfd5a1f7ce.
Report an issue: GitHub.
Appendix: source
Thrown at src/Data/DLLManager.cs:173
var fileDownloader = new FileDownloader("https://beeradmoore.github.io/dlss-swapper/manifest.json", 0);
await fileDownloader.DownloadFileToStreamAsync(memoryStream);
memoryStream.Position = 0;
var newManifestHash = memoryStream.GetMD5Hash();
// If the old manifest on disk is the same as the new one there is no need to do anything as it will already be loaded.
if (oldManifestHash == newManifestHash)
{
return true;
}
memoryStream.Position = 0;
var manifest = await JsonSerializer.DeserializeAsync(memoryStream, SourceGenerationContext.Default.Manifest);
if (manifest is null)
{
throw new Exception("Could not deserialize manifest.json.");
}
Manifest = manifest;
try
{
Storage.CreateDirectoryForFileIfNotExists(manifestPath);
using (var stream = File.Create(manifestPath))
{
memoryStream.Position = 0;
memoryStream.CopyTo(stream);
}
}
catch (Exception err)
{
Logger.Error(err);
Debugger.Break();
}View on GitHub (pinned to ab9b1e2d4b)