d2phap/ImageGlass · warning · InvalidOperationException

IGE: Metadata too large: {contentLength} bytes.

Error message

IGE: Metadata too large: {contentLength} bytes.

What it means

Thrown by UpdateProvider when fetching update metadata: the HTTP response's Content-Length header exceeds UpdateConstants.MaxMetadataSize (1 MiB, defined in Update/UpdateConstants.cs:44). The size limit exists because update metadata is a small JSON document, so anything larger is either a misconfigured endpoint or a malicious/broken payload that should not be fully buffered into memory.

Source

Thrown at source/ImageGlass.Lib/Common/ServiceProviders/UpdateProvider.cs:168

    private static async Task<string?> FetchMetadataAsync(bool isScheduled, CancellationToken ct)
    {
        using var timeoutCts = CancellationTokenSource.CreateLinkedTokenSource(ct);
        timeoutCts.CancelAfter(UpdateConstants.MetadataTimeout);

        using var request = new HttpRequestMessage(HttpMethod.Get, UpdateConstants.MetadataUrl);

        // set per request: the value varies per check, so it cannot live on the shared client
        request.Headers.UserAgent.ParseAdd(UsageStatsAgent.Build(isScheduled));

        using var response = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, timeoutCts.Token)
            .ConfigureAwait(false);
        response.EnsureSuccessStatusCode();

        // enforce size limit
        var contentLength = response.Content.Headers.ContentLength ?? 0;
        if (contentLength > UpdateConstants.MaxMetadataSize)
        {
            throw new InvalidOperationException($"IGE: Metadata too large: {contentLength} bytes.");
        }

        var json = await response.Content.ReadAsStringAsync(timeoutCts.Token).ConfigureAwait(false);
        if (json.Length > UpdateConstants.MaxMetadataSize)
        {
            throw new InvalidOperationException($"IGE: Metadata body exceeded limit: {json.Length} chars.");
        }

        return json;
    }


    /// <summary>
    /// Parses the last check time from <see cref="Core.Config.AutoUpdate"/>.
    /// </summary>
    private static DateTime ParseLastCheckTime()
    {
        var value = Core.Config.AutoUpdate;

View on GitHub (pinned to 4a3c4fecef)

Solutions

  1. Verify the configured update metadata URL points at the real ImageGlass update manifest and not a HTML page or error payload.
  2. If you self-host the manifest, ensure the server returns only the JSON document and that Content-Length is under 1 MiB; remove any injected wrapping page.
  3. Catch InvalidOperationException around the update check and surface a user-facing 'update check failed' message rather than crashing; retry on the next scheduled check.
  4. If the limit is genuinely too small for your manifest, raise UpdateConstants.MaxMetadataSize in Update/UpdateConstants.cs:44 (but 1 MiB is already very large for JSON metadata).

Example fix

// before
using var response = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, timeoutCts.Token);
response.EnsureSuccessStatusCode();
var contentLength = response.Content.Headers.ContentLength ?? 0;
if (contentLength > UpdateConstants.MaxMetadataSize) throw new InvalidOperationException(...);

// after — reject non-JSON early so a CDN HTML page never reaches the size check
using var response = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, timeoutCts.Token);
response.EnsureSuccessStatusCode();
var ct = response.Content.Headers.ContentType?.MediaType;
if (!string.Equals(ct, "application/json", StringComparison.OrdinalIgnoreCase))
    throw new InvalidOperationException("IGE: Update endpoint did not return JSON.");
var contentLength = response.Content.Headers.ContentLength ?? 0;
if (contentLength > UpdateConstants.MaxMetadataSize) throw new InvalidOperationException(...);
Defensive patterns

Strategy: validation

Validate before calling

// Before calling the update-check API, validate the endpoint and request shape.
if (string.IsNullOrWhiteSpace(updateUrl) || !Uri.TryCreate(updateUrl, UriKind.Absolute, out var uri) || uri.Scheme != Uri.UriSchemeHttps)
    return UpdateCheckResult.Skipped("Invalid update URL");

// After the response arrives, check Content-Type and Content-Length before buffering.
var media = response.Content.Headers.ContentType?.MediaType;
if (!string.Equals(media, "application/json", StringComparison.OrdinalIgnoreCase))
    return UpdateCheckResult.Skipped($"Unexpected content type {media}");
var len = response.Content.Headers.ContentLength ?? -1;
if (len > UpdateConstants.MaxMetadataSize)
    return UpdateCheckResult.Skipped($"Metadata too large: {len} bytes");

Try / catch

try { var json = await updateProvider.FetchMetadataAsync(token); }
catch (InvalidOperationException ex) when (ex.Message.Contains("Metadata too large"))
{ _log.Warn($"Update metadata oversize, skipping check: {ex.Message}"); return; }

Prevention

When it happens

Trigger: Produced by UpdateProvider's metadata fetch path: after response.EnsureSuccessStatusCode() the code reads response.Content.Headers.ContentLength and throws when that value > 1 * 1024 * 1024. Triggers when the server advertises a large body for the update JSON URL, or when a CDN/reverse proxy in front of the real endpoint serves the wrong (oversized) resource.

Common situations: Update endpoint behind a CDN that returns an HTML error page (which can be large) instead of JSON; a typo'd/migrated update URL now pointing at a HTML landing page; a malicious mirror replacing the manifest with a large file; corporate proxy injecting a large block page; a staging server accidentally returning a debug bundle.

Related errors


AI-assisted analysis of d2phap/ImageGlass@4a3c4fecef (2026-08-13). Data as JSON: /api/errors/d7f197cc945f719b. Report an issue: GitHub.