beeradmoore/dlss-swapper · error · Exception
Could not deserialize GOGCatalogResponse for url
Error message
Could not deserialize GOGCatalogResponse for url, {url} What it means
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.
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
Example fix
// before
var catalogResponse = JsonSerializer.Deserialize(memoryStream, SourceGenerationContext.Default.GOGCatalogResponse);
if (catalogResponse is null)
{
throw new Exception($"Could not deserialize GOGCatalogResponse for url, {url}");
}
// after
catalogResponse = JsonSerializer.Deserialize(memoryStream, SourceGenerationContext.Default.GOGCatalogResponse);
if (catalogResponse is null)
{
throw new Exception($"Could not deserialize GOGCatalogResponse for url, {url}. Body length: {memoryStream.Length}");
} Defensive patterns
Strategy: try-catch
Validate before calling
// preflight the catalog URL
using var probe = await httpClient.GetAsync(url);
var body = await probe.Content.ReadAsStringAsync();
if (!body.TrimStart().StartsWith("{"))
throw new InvalidOperationException($"GOG catalog returned non-JSON for {url}."); Type guard
static bool LooksLikeGogCatalog(string json) => json.TrimStart().StartsWith("{") && json.Contains("products", StringComparison.OrdinalIgnoreCase); Try / catch
try
{
await gogGame.UpdateCacheImageAsync();
}
catch (Exception ex) when (ex.Message.Contains("Could not deserialize GOGCatalogResponse"))
{
logger.LogWarning(ex, "GOG catalog unavailable for {Url}; skipping cache image.", url);
} Prevention
- 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
When it happens
Trigger: 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).
Common situations: 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.
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 GOGEmbedFilteredResponse for url
- Could not deserialize manifest.json.
- Could not find any GOGEmbedFilteredProducts for url
- Could not load GitHub release data.
- Could not download file.
AI-assisted analysis of beeradmoore/dlss-swapper@ab9b1e2d4b (2026-09-15).
Data as JSON: /api/errors/996baaef1894d302.
Report an issue: GitHub.
Appendix: source
Thrown at src/Data/GOG/GOGGame.cs:78
// Some games are not found here (eg. Warecraft III) and instead will fallback
// to using the direct product loading below. Unfortuantly using the product
// endpoint does not contain boxart image urls so the images are not really
// what we want, but at least there is images.
try
{
var url = "https://catalog.gog.com/v1/catalog?order=desc:score&productType=in:game&query=like:" + Uri.EscapeDataString(Title);
var fileDownloader = new FileDownloader(url);
using (var memoryStream = new MemoryStream())
{
await fileDownloader.DownloadFileToStreamAsync(memoryStream);
memoryStream.Position = 0;
var catalogResponse = JsonSerializer.Deserialize(memoryStream, SourceGenerationContext.Default.GOGCatalogResponse);
if (catalogResponse is null)
{
throw new Exception($"Could not deserialize GOGCatalogResponse for url, {url}");
}
if (catalogResponse.Products.Length == 0)
{
throw new Exception($"Could not find any GOGCatalogProduct for url, {url}");
}
foreach (var product in catalogResponse.Products)
{
if (product.Id.Equals(PlatformId, StringComparison.OrdinalIgnoreCase) == false)
{
continue;
}
if (string.IsNullOrWhiteSpace(product.CoverVertical) == false)
{
await DownloadCoverAsync(product.CoverVertical).ConfigureAwait(false);
return;View on GitHub (pinned to ab9b1e2d4b)