{"record":{"id":"b014164146472f17","repo":"d2phap/ImageGlass","slug":"ige-metadata-body-exceeded-limit-json-length-c","errorCode":null,"errorMessage":"IGE: Metadata body exceeded limit: {json.Length} chars.","messagePattern":"IGE: Metadata body exceeded limit: (.+?) chars\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"warning","filePath":"source/ImageGlass.Lib/Common/ServiceProviders/UpdateProvider.cs","lineNumber":174,"sourceCode":"\n        // set per request: the value varies per check, so it cannot live on the shared client\n        request.Headers.UserAgent.ParseAdd(UsageStatsAgent.Build(isScheduled));\n\n        using var response = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, timeoutCts.Token)\n            .ConfigureAwait(false);\n        response.EnsureSuccessStatusCode();\n\n        // enforce size limit\n        var contentLength = response.Content.Headers.ContentLength ?? 0;\n        if (contentLength > UpdateConstants.MaxMetadataSize)\n        {\n            throw new InvalidOperationException($\"IGE: Metadata too large: {contentLength} bytes.\");\n        }\n\n        var json = await response.Content.ReadAsStringAsync(timeoutCts.Token).ConfigureAwait(false);\n        if (json.Length > UpdateConstants.MaxMetadataSize)\n        {\n            throw new InvalidOperationException($\"IGE: Metadata body exceeded limit: {json.Length} chars.\");\n        }\n\n        return json;\n    }\n\n\n    /// <summary>\n    /// Parses the last check time from <see cref=\"Core.Config.AutoUpdate\"/>.\n    /// </summary>\n    private static DateTime ParseLastCheckTime()\n    {\n        var value = Core.Config.AutoUpdate;\n        if (string.IsNullOrEmpty(value) || string.Equals(value, \"0\", StringComparison.Ordinal))\n        {\n            return DateTime.MinValue;\n        }\n\n        return DateTime.TryParse(value, out var dt) ? dt.ToUniversalTime() : DateTime.MinValue;","sourceCodeStart":156,"sourceCodeEnd":192,"githubUrl":"https://github.com/d2phap/ImageGlass/blob/4a3c4feceffc5a8bb5e56ba836509634aaae47a9/source/ImageGlass.Lib/Common/ServiceProviders/UpdateProvider.cs#L156-L192","documentation":"Second of two size guards in UpdateProvider's metadata fetch. After the Content-Length header passes (or is missing/zero), the response body is fully read into a string and re-checked against UpdateConstants.MaxMetadataSize (1 MiB). This catches servers that lie about or omit Content-Length while still streaming an oversized body.","triggerScenarios":"Produced when json.Length > MaxMetadataSize after ReadAsStringAsync completes. Happens when the server sends no Content-Length (chunked transfer encoding) so the header check at line 168 was skipped, or when the actual body is larger than the advertised Content-Length.","commonSituations":"Update endpoint serving chunked/gzip-compressed data with no Content-Length; a reverse proxy or middleware that strips Content-Length; an endpoint that streams a JSON array that grows over time past the 1 MiB mark; a man-in-the-middle injecting extra content.","solutions":["Inspect the actual response body to see what is being served (curl the update URL with -v and -o) and confirm it is the intended JSON manifest.","Ensure the upstream server sets an accurate Content-Length and serves the file uncompressed or with Content-Encoding handled at the transport layer.","Catch the InvalidDataException/InvalidOperationException around the update check and degrade gracefully (skip this update check, log, retry later).","If legitimate metadata exceeds 1 MiB, raise UpdateConstants.MaxMetadataSize in Update/UpdateConstants.cs:44 — but first verify nothing else (a wrong URL) is the cause."],"exampleFix":"// before\nvar json = await response.Content.ReadAsStringAsync(timeoutCts.Token).ConfigureAwait(false);\nif (json.Length > UpdateConstants.MaxMetadataSize) throw new InvalidOperationException(...);\n\n// after — stream-read with a hard cap so a runaway body never fully buffers\nusing var fs = await response.Content.ReadAsStreamAsync(timeoutCts.Token).ConfigureAwait(false);\nusing var ms = new MemoryStream(checked((int)Math.Min(contentLength, UpdateConstants.MaxMetadataSize + 1)));\nvar buf = new byte[8192];\nint n;\nwhile ((n = await fs.ReadAsync(buf, timeoutCts.Token)) > 0) {\n    ms.Write(buf, 0, n);\n    if (ms.Length > UpdateConstants.MaxMetadataSize) throw new InvalidOperationException($\"IGE: Metadata body exceeded limit after {ms.Length} bytes.\");\n}\nvar json = Encoding.UTF8.GetString(ms.GetBuffer(), 0, (int)ms.Length);","handlingStrategy":"validation","validationCode":"// Read with a hard cap so a runaway body never fully buffers into RAM.\nawait using var stream = await response.Content.ReadAsStreamAsync(token);\nusing var capped = new MemoryStream();\nvar buf = new byte[8192]; int n;\nwhile ((n = await stream.ReadAsync(buf, token)) > 0)\n{\n    capped.Write(buf, 0, n);\n    if (capped.Length > UpdateConstants.MaxMetadataSize)\n        return UpdateCheckResult.Skipped(\"Metadata stream exceeded limit\");\n}\nvar json = Encoding.UTF8.GetString(capped.GetBuffer(), 0, (int)capped.Length);","typeGuard":null,"tryCatchPattern":"try { var json = await updateProvider.FetchMetadataAsync(token); }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"body exceeded limit\"))\n{ _log.Warn($\"Update metadata body oversize (chunked/no Content-Length), skipping: {ex.Message}\"); return; }","preventionTips":["Stream the body with a size cap instead of buffering all of it before checking.","Distrust missing Content-Length: always enforce the cap on bytes read, not just on the header.","Verify the upstream server sets accurate Content-Length and serves the manifest directly (no wrapping HTML).","Log the actual content type and first bytes when this fires so the wrong-endpoint case is obvious."],"tags":["network","http","update","size-limit","streaming"],"backgroundTag":null,"analyzedSha":"4a3c4feceffc5a8bb5e56ba836509634aaae47a9","analyzedAt":"2026-08-13T16:58:15.523Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}