{"record":{"id":"431763dfd5a1f7ce","repo":"beeradmoore/dlss-swapper","slug":"could-not-deserialize-manifest-json","errorCode":null,"errorMessage":"Could not deserialize manifest.json.","messagePattern":"Could not deserialize manifest\\.json\\.","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"src/Data/DLLManager.cs","lineNumber":173,"sourceCode":"                var fileDownloader = new FileDownloader(\"https://beeradmoore.github.io/dlss-swapper/manifest.json\", 0);\n                await fileDownloader.DownloadFileToStreamAsync(memoryStream);\n\n                memoryStream.Position = 0;\n\n                var newManifestHash = memoryStream.GetMD5Hash();\n\n                // 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.\n                if (oldManifestHash == newManifestHash)\n                {\n                    return true;\n                }\n\n                memoryStream.Position = 0;\n\n                var manifest = await JsonSerializer.DeserializeAsync(memoryStream, SourceGenerationContext.Default.Manifest);\n                if (manifest is null)\n                {\n                    throw new Exception(\"Could not deserialize manifest.json.\");\n                }\n\n                Manifest = manifest;\n\n                try\n                {\n                    Storage.CreateDirectoryForFileIfNotExists(manifestPath);\n                    using (var stream = File.Create(manifestPath))\n                    {\n                        memoryStream.Position = 0;\n                        memoryStream.CopyTo(stream);\n                    }\n                }\n                catch (Exception err)\n                {\n                    Logger.Error(err);\n                    Debugger.Break();\n                }","sourceCodeStart":155,"sourceCodeEnd":191,"githubUrl":"https://github.com/beeradmoore/dlss-swapper/blob/ab9b1e2d4bb04187c48fe58eeea06c2b0ea71fbb/src/Data/DLLManager.cs#L155-L191","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nvar manifest = await JsonSerializer.DeserializeAsync(memoryStream, SourceGenerationContext.Default.Manifest);\nif (manifest is null)\n{\n    throw new Exception(\"Could not deserialize manifest.json.\");\n}\n// after\nif (memoryStream.Length == 0)\n{\n    throw new Exception($\"Manifest download was empty ({manifestUrl}).\");\n}\nvar manifest = await JsonSerializer.DeserializeAsync(memoryStream, SourceGenerationContext.Default.Manifest);\nif (manifest is null)\n{\n    throw new Exception($\"Could not deserialize manifest.json from {manifestUrl} (empty body).\");\n}","handlingStrategy":"try-catch","validationCode":"// before calling UpdateManifestAsync\nusing var head = await httpClient.GetAsync(manifestUrl, HttpCompletionOption.ResponseHeadersRead);\nif (!head.IsSuccessStatusCode || head.Content.Headers.ContentLength == 0)\n    throw new InvalidOperationException($\"Manifest at {manifestUrl} is empty or unreachable.\");","typeGuard":"static bool HasManifestContent(Stream s) => s.CanSeek && s.Length > 0;","tryCatchPattern":"try\n{\n    await dllManager.UpdateManifestAsync();\n}\ncatch (Exception ex) when (ex.Message.Contains(\"Could not deserialize manifest.json\"))\n{\n    logger.LogWarning(ex, \"Manifest download/parse failed; keeping existing manifest.\");\n}","preventionTips":["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"],"tags":["json","deserialization","manifest","dotnet"],"backgroundTag":"json-unmarshal-failed","analyzedSha":"ab9b1e2d4bb04187c48fe58eeea06c2b0ea71fbb","analyzedAt":"2026-09-15T05:25:32.979Z","contentChangedAt":"2026-09-15T05:25:32.979Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}