{"record":{"id":"996baaef1894d302","repo":"beeradmoore/dlss-swapper","slug":"could-not-deserialize-gogcatalogresponse-for-url-url","errorCode":null,"errorMessage":"Could not deserialize GOGCatalogResponse for url, {url}","messagePattern":"Could not deserialize GOGCatalogResponse for url, (.+?)","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"src/Data/GOG/GOGGame.cs","lineNumber":78,"sourceCode":"        // Some games are not found here (eg. Warecraft III) and instead will fallback\n        // to using the direct product loading below. Unfortuantly using the product\n        // endpoint does not contain boxart image urls so the images are not really\n        // what we want, but at least there is images.\n\n\n        try\n        {\n            var url = \"https://catalog.gog.com/v1/catalog?order=desc:score&productType=in:game&query=like:\" + Uri.EscapeDataString(Title);\n            var fileDownloader = new FileDownloader(url);\n            using (var memoryStream = new MemoryStream())\n            {\n                await fileDownloader.DownloadFileToStreamAsync(memoryStream);\n                memoryStream.Position = 0;\n\n                var catalogResponse = JsonSerializer.Deserialize(memoryStream, SourceGenerationContext.Default.GOGCatalogResponse);\n                if (catalogResponse is null)\n                {\n                    throw new Exception($\"Could not deserialize GOGCatalogResponse for url, {url}\");\n                }\n\n                if (catalogResponse.Products.Length == 0)\n                {\n                    throw new Exception($\"Could not find any GOGCatalogProduct for url, {url}\");\n                }\n\n                foreach (var product in catalogResponse.Products)\n                {\n                    if (product.Id.Equals(PlatformId, StringComparison.OrdinalIgnoreCase) == false)\n                    {\n                        continue;\n                    }\n\n                    if (string.IsNullOrWhiteSpace(product.CoverVertical) == false)\n                    {\n                        await DownloadCoverAsync(product.CoverVertical).ConfigureAwait(false);\n                        return;","sourceCodeStart":60,"sourceCodeEnd":96,"githubUrl":"https://github.com/beeradmoore/dlss-swapper/blob/ab9b1e2d4bb04187c48fe58eeea06c2b0ea71fbb/src/Data/GOG/GOGGame.cs#L60-L96","documentation":"GOGGame.UpdateCacheImageAsync downloads a GOG catalog lookup for the game's URL and deserializes it into GOGCatalogResponse with System.Text.Json; a null result throws this error. Null means the response body was empty or not JSON matching the GOGCatalogResponse contract, so the cache image update cannot proceed.","triggerScenarios":"Calling UpdateCacheImageAsync when the GOG catalog endpoint returns an empty body, an HTML error page, or JSON whose shape doesn't satisfy the source-generated GOGCatalogResponse deserializer (leading to null/empty stream).","commonSituations":"GOG API changes or downtime; wrong/removed game URL or platform id; region-locked or 403 page returned instead of JSON; connectivity issues yielding an empty stream.","solutions":["Verify the catalog URL for this game returns valid JSON (curl and inspect)","Check GOG API status / retry later on transient failures","Confirm PlatformId and the constructed URL are correct for the game","Wrap deserialization to catch JsonException and log the raw body for diagnosis"],"exampleFix":"// before\nvar catalogResponse = JsonSerializer.Deserialize(memoryStream, SourceGenerationContext.Default.GOGCatalogResponse);\nif (catalogResponse is null)\n{\n    throw new Exception($\"Could not deserialize GOGCatalogResponse for url, {url}\");\n}\n// after\ncatalogResponse = JsonSerializer.Deserialize(memoryStream, SourceGenerationContext.Default.GOGCatalogResponse);\nif (catalogResponse is null)\n{\n    throw new Exception($\"Could not deserialize GOGCatalogResponse for url, {url}. Body length: {memoryStream.Length}\");\n}","handlingStrategy":"try-catch","validationCode":"// preflight the catalog URL\nusing var probe = await httpClient.GetAsync(url);\nvar body = await probe.Content.ReadAsStringAsync();\nif (!body.TrimStart().StartsWith(\"{\"))\n    throw new InvalidOperationException($\"GOG catalog returned non-JSON for {url}.\");","typeGuard":"static bool LooksLikeGogCatalog(string json) => json.TrimStart().StartsWith(\"{\") && json.Contains(\"products\", StringComparison.OrdinalIgnoreCase);","tryCatchPattern":"try\n{\n    await gogGame.UpdateCacheImageAsync();\n}\ncatch (Exception ex) when (ex.Message.Contains(\"Could not deserialize GOGCatalogResponse\"))\n{\n    logger.LogWarning(ex, \"GOG catalog unavailable for {Url}; skipping cache image.\", url);\n}","preventionTips":["Log raw response bodies when deserialization returns null","Treat cache-image updates as best-effort and degrade gracefully","Retry on transient GOG outages with backoff","Pin/monitor the GOG API contract used by SourceGenerationContext"],"tags":["gog","json","deserialization","http"],"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"}