iOfficeAI/OfficeCLI · error · ArgumentException

Remote image exceeds {SsrfGuard.MaxRemoteBytes / (1024 * 102

Error message

Remote image exceeds {SsrfGuard.MaxRemoteBytes / (1024 * 1024)} MB limit.

What it means

Thrown by ImageSource.ResolveUrl when a remote image download exceeds the shared size cap defined in SsrfGuard.MaxRemoteBytes (100 MB). The check fires in two places: first against the Content-Length response header (if the server declares it), and second during streaming via SsrfGuard.ReadBounded which enforces the cap even when Content-Length is absent. The limit is shared between image fetch and file fetch so policy cannot diverge.

Source

Thrown at src/officecli/Core/ImageSource.cs:164

    private static (Stream, PartTypeInfo) ResolveUrl(string url)
    {
        // SSRF guard lives in the shared SsrfGuard so image and file fetch can
        // never diverge in policy. See SsrfGuard for the connect-time / redirect
        // / DNS-rebinding rationale.
        var handler = SsrfGuard.CreateGuardedHandler("image");

        using var client = new HttpClient(handler, disposeHandler: true) { Timeout = TimeSpan.FromSeconds(30) };
        client.DefaultRequestHeaders.Add("User-Agent", "OfficeCLI");

        var response = client.GetAsync(url).GetAwaiter().GetResult();
        response.EnsureSuccessStatusCode();

        // Enforce the shared size cap whether or not the server sends
        // Content-Length. ReadBounded lives in SsrfGuard so image and file
        // fetch share one limit (see SsrfGuard.MaxRemoteBytes).
        var declared = response.Content.Headers.ContentLength;
        if (declared is > SsrfGuard.MaxRemoteBytes)
            throw new ArgumentException($"Remote image exceeds {SsrfGuard.MaxRemoteBytes / (1024 * 1024)} MB limit.");
        var bytes = SsrfGuard.ReadBounded(response.Content.ReadAsStream(), SsrfGuard.MaxRemoteBytes, url, "image");
        var stream = new MemoryStream(bytes);

        // Try content-type header first
        var serverMime = response.Content.Headers.ContentType?.MediaType;
        if (!string.IsNullOrEmpty(serverMime) && TryMimeToContentType(serverMime, out var ct))
            return (stream, ct);

        // Fallback: extract extension from URL path (strip query string)
        var uri = new Uri(url);
        var ext = Path.GetExtension(uri.AbsolutePath);
        if (!string.IsNullOrEmpty(ext))
            return (stream, ExtensionToContentType(ext));

        // Last resort: sniff magic bytes
        if (TrySniffContentType(bytes, out var sniffed))
            return (stream, sniffed);

View on GitHub (pinned to 1ced45e900)

Solutions

  1. Download the image locally, resize/compress it to under 100 MB, and embed the local file instead.
  2. Use a URL that points to a smaller version or thumbnail of the image.
  3. If the limit is genuinely too low for your use case, host a pre-resized image at a different URL.

Example fix

// before — remote image too large
add image src='https://example.com/huge-tiff-200mb.tif' path='/body'

// after — download, compress, embed locally
// wget -O /tmp/img.tif 'https://example.com/huge-tiff-200mb.tif'
// magick /tmp/img.tif -resize 50% -compress LZW /tmp/img-small.tif
add image src='/tmp/img-small.tif' path='/body'
Defensive patterns

Strategy: validation

Validate before calling

// Pre-check: HEAD the URL to get Content-Length before full download
using var client = new HttpClient();
var headResp = await client.SendAsync(new HttpRequestMessage(HttpMethod.Head, url));
if (headResp.Content.Headers.ContentLength is long len && len > SsrfGuard.MaxRemoteBytes)
{
    Console.Error.WriteLine($"Remote image is {len / (1024*1024)} MB, exceeds the {SsrfGuard.MaxRemoteBytes / (1024*1024)} MB limit.");
    return;
}

Try / catch

try
{
    var (stream, contentType) = ImageSource.Resolve(url);
}
catch (ArgumentException ex) when (ex.Message.Contains("MB limit"))
{
    // Download locally, compress/resize, embed as local file
}

Prevention

When it happens

Trigger: Calling ImageSource.Resolve with an HTTP(S) URL pointing to an image larger than 100 MB. The declared Content-Length header value exceeds MaxRemoteBytes, or the streamed bytes exceed the cap during ReadBounded. The SSRF guard handler wraps the HttpClient for DNS-rebinding and redirect protection, and the size cap is enforced on top.

Common situations: Linking to a high-resolution stock photo or satellite imagery that exceeds 100 MB. A URL that redirects to a much larger payload than expected. A misconfigured server returning an incorrect Content-Length or no Content-Length with a very large body.

Related errors


AI-assisted analysis of iOfficeAI/OfficeCLI@1ced45e900 (2026-08-13). Data as JSON: /api/errors/abbb1535007d9de8. Report an issue: GitHub.