{"record":{"id":"e296a9c65d0581bb","repo":"beeradmoore/dlss-swapper","slug":"could-not-load-github-release-data","errorCode":null,"errorMessage":"Could not load GitHub release data.","messagePattern":"Could not load GitHub release data\\.","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"src/Data/GitHub/GitHubUpdater.cs","lineNumber":71,"sourceCode":"                }\n            }\n        }\n\n        if (shouldDownload == true || forceCheck == true)\n        {\n            try\n            {\n                using (var memoryStream = new MemoryStream())\n                {\n                    var fileDownloader = new FileDownloader(\"https://api.github.com/repos/beeradmoore/dlss-swapper/releases/latest\", 0);\n                    await fileDownloader.DownloadFileToStreamAsync(memoryStream).ConfigureAwait(false);\n\n                    memoryStream.Position = 0;\n\n                    var githubRelease = JsonSerializer.Deserialize(memoryStream, SourceGenerationContext.Default.GitHubRelease);\n                    if (githubRelease is null)\n                    {\n                        throw new Exception(\"Could not load GitHub release data.\");\n                    }\n\n                    memoryStream.Position = 0;\n\n                    // If we did load the json, save it to disk.\n                    using (var fileStream = File.Create(releasesFile))\n                    {\n                        await memoryStream.CopyToAsync(fileStream).ConfigureAwait(false);\n                    }\n\n                    return githubRelease;\n                }\n            }\n            catch (Exception err)\n            {\n                // NOOP\n                Logger.Error(err);\n                return null;","sourceCodeStart":53,"sourceCodeEnd":89,"githubUrl":"https://github.com/beeradmoore/dlss-swapper/blob/ab9b1e2d4bb04187c48fe58eeea06c2b0ea71fbb/src/Data/GitHub/GitHubUpdater.cs#L53-L89","documentation":"FetchLatestRelease downloads the GitHub releases JSON into a memory stream and deserializes it with the source-generated JsonSerializer (SourceGenerationContext.Default.GitHubRelease). If deserialization succeeds but returns null, the code throws because there is no usable release data to compare versions or save to the releases cache file.","triggerScenarios":"The GitHub API returns a body that deserializes to null for GitHubRelease - e.g. an empty or non-object JSON body (rate-limit message, error JSON) that JsonSerializer.Deserialize accepts but cannot map to a GitHubRelease instance.","commonSituations":"GitHub API rate limiting (403 with a JSON error body), a proxy or captive portal returning an empty body or HTML, GitHub incident/outage, or an unexpectedly shaped API response after a GitHub API change.","solutions":["Check the HTTP status code before deserializing; only parse the body when status is 200 OK.","Check GitHub API rate limit (curl -s https://api.github.com/rate_limit) and wait for the window to reset; consider authenticating requests.","Delete the cached releases file so the next check re-downloads fresh release data.","Log the raw response body on failure to see what GitHub actually returned, and update the GitHubRelease model if the API shape changed."],"exampleFix":"// before\nvar githubRelease = JsonSerializer.Deserialize(memoryStream, SourceGenerationContext.Default.GitHubRelease);\nif (githubRelease is null)\n    throw new Exception(\"Could not load GitHub release data.\");\n// after\nif (response.StatusCode != HttpStatusCode.OK)\n    throw new Exception($\"GitHub API returned {response.StatusCode}; cannot load release data.\");\nvar githubRelease = JsonSerializer.Deserialize(memoryStream, SourceGenerationContext.Default.GitHubRelease);\nif (githubRelease is null)\n    throw new Exception(\"Could not load GitHub release data.\");","handlingStrategy":"try-catch","validationCode":"// Check HTTP status and rate limit before deserializing\nif (response.StatusCode == HttpStatusCode.Forbidden)\n{\n    // likely GitHub API rate limit; abort or back off\n}","typeGuard":"bool IsValidRelease(GitHubRelease? r) => r is not null && !string.IsNullOrEmpty(r.TagName);","tryCatchPattern":"try\n{\n    var release = await updater.FetchLatestRelease();\n}\ncatch (Exception ex) when (ex.Message == \"Could not load GitHub release data.\")\n{\n    logger.LogWarning(ex, \"GitHub release check failed; using cached release info\");\n}","preventionTips":["Only deserialize the release body after confirming HTTP 200","Handle GitHub rate-limit responses (403/429) with backoff before parsing","Keep the on-disk releases cache as a fallback when the API fails"],"tags":["github","json","deserialization","update-check"],"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"}