iOfficeAI/OfficeCLI · error · ArgumentException
Remote {what} at {url} exceeds {max / (1024 * 1024)} MB limi
Error message
Remote {what} at {url} exceeds {max / (1024 * 1024)} MB limit. What it means
Thrown by SsrfGuard.ReadBounded once the remote body has streamed more than the byte cap (default SsrfGuard.MaxRemoteBytes = 100 MB). It bounds memory on a hostile or accidentally-huge response because the default HttpClient buffering ceiling (~2 GB) is far above any legitimate asset. Callers are expected to also pre-check Content-Length to fail fast on honest servers.
Source
Thrown at src/officecli/Core/SsrfGuard.cs:98
/// <summary>
/// Copy <paramref name="src"/> into memory, refusing once <paramref name="max"/>
/// bytes have been read. Use after <see cref="CreateGuardedHandler"/> so a
/// chunked / Content-Length-lying response cannot exhaust memory. Callers
/// should still pre-check <c>response.Content.Headers.ContentLength</c> to
/// fail fast when the server is honest about an oversized body.
/// </summary>
/// <param name="what">Noun used in the refusal message, e.g. "image" or "file".</param>
public static byte[] ReadBounded(Stream src, long max, string url, string what = "file")
{
using var ms = new MemoryStream();
var buf = new byte[81920];
long total = 0;
int n;
while ((n = src.Read(buf, 0, buf.Length)) > 0)
{
total += n;
if (total > max)
throw new ArgumentException(
$"Remote {what} at {url} exceeds {max / (1024 * 1024)} MB limit.");
ms.Write(buf, 0, n);
}
return ms.ToArray();
}
/// <summary>
/// True only for globally-routable addresses. Blocks loopback, private
/// (RFC1918), link-local (incl. 169.254.0.0/16 cloud-metadata), unique-local
/// IPv6 (fc00::/7), multicast and unspecified — the SSRF target ranges.
/// </summary>
public static bool IsPublicAddress(IPAddress address)
{
var addr = address.IsIPv4MappedToIPv6 ? address.MapToIPv4() : address;
if (IPAddress.IsLoopback(addr)) return false;
if (addr.Equals(IPAddress.Any) || addr.Equals(IPAddress.IPv6Any)) return false;
View on GitHub (pinned to 1ced45e900)
Solutions
- Point at a smaller, appropriately-sized asset (resize images, trim data).
- Pre-check response.Content.Headers.ContentLength against your limit and reject before streaming.
- Host the file locally instead of fetching a huge remote blob.
Example fix
// before (client side, before ReadBounded) var resp = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead); var len = resp.Content.Headers.ContentLength ?? -1; if (len is 0 or -1 || len > SsrfGuard.MaxRemoteBytes) return; // fail fast on honest servers using var s = await resp.Content.ReadAsStreamAsync(); var bytes = SsrfGuard.ReadBounded(s, SsrfGuard.MaxRemoteBytes, url);
Defensive patterns
Strategy: validation
Validate before calling
// Pre-check Content-Length when the server is honest about size
long len = resp.Content.Headers.ContentLength ?? -1;
if (len > SsrfGuard.MaxRemoteBytes)
throw new InvalidOperationException("remote asset too large before download"); Try / catch
try { bytes = SsrfGuard.ReadBounded(stream, max, url, "image"); }
catch (System.ArgumentException ex) when (ex.Message.Contains("MB limit"))
{ /* asset exceeded the size cap; use a smaller source */ } Prevention
- Always request ResponseHeadersRead and check Content-Length first.
- Cap and downscale images at the source/CDN.
- Reject chunked streams of unknown length for untrusted URLs.
When it happens
Trigger: An image=/data=/media=/model3d= URL whose response body exceeds the cap; a streaming/chunked response with no Content-Length that keeps sending; pointing at a large downloadable file by mistake.
Common situations: Linking to a full-resolution raw asset; a CDN returning a larger variant than expected; an unbounded server stream.
Related errors
- Refusing to fetch {what} from non-public address '{addr}' (h
- Could not resolve host '{host}'.
- body too large
- Checksum mismatch for ${asset} (expected ${expected}, got ${
- Could not download OfficeCLI binary (${asset} @ ${TAG}). Las
AI-assisted analysis of iOfficeAI/OfficeCLI@1ced45e900 (2026-08-13).
Data as JSON: /api/errors/5b3174b6e12823bd.
Report an issue: GitHub.