iOfficeAI/OfficeCLI · error · InvalidDataException
body too large
Error message
body too large
What it means
Thrown by WatchServer.ReadPostBodyAsync when a POST selection request's Content-Length is negative or exceeds MaxSelectionBodyBytes (InvalidDataException). It is a slow-loris / memory-exhaustion guard on the watch server's HTTP surface (FUZZER-001). A prefix that already overshoots Content-Length is trimmed rather than rejected, but an oversized declared length is refused outright.
Source
Thrown at src/officecli/Core/Watch/WatchServer.cs:2219
/// straddles a read boundary into two independent fragments, and each
/// fragment decodes to U+FFFD — a large CJK payload came back from a
/// 200 OK with characters silently replaced, and the replacements went
/// straight into the document. Every /api POST shares this path so the
/// bound checks stay identical across endpoints.
///
/// Bounded by MaxSelectionBodyBytes (FUZZER-001 slow-loris) and
/// PostBodyReadTimeout. A prefix overshooting Content-Length is trimmed:
/// otherwise extra bytes could be smuggled in the header segment
/// (FUZZER-002). A request with no Content-Length keeps the prefix as-is.
/// </summary>
private static async Task<string> ReadPostBodyAsync(
NetworkStream stream, Dictionary<string, string> headers, byte[] bodyPrefix, CancellationToken token)
{
int contentLength = -1;
if (headers.TryGetValue("Content-Length", out var clStr) && int.TryParse(clStr, out var parsedCl))
{
if (parsedCl < 0 || parsedCl > MaxSelectionBodyBytes)
throw new InvalidDataException("body too large");
contentLength = parsedCl;
}
if (contentLength < 0) return Encoding.UTF8.GetString(bodyPrefix);
if (bodyPrefix.Length >= contentLength) return Encoding.UTF8.GetString(bodyPrefix, 0, contentLength);
var body = new byte[contentLength];
bodyPrefix.CopyTo(body, 0);
int have = bodyPrefix.Length;
using var readCts = CancellationTokenSource.CreateLinkedTokenSource(token);
readCts.CancelAfter(PostBodyReadTimeout);
try
{
while (have < contentLength)
{
var n = await stream.ReadAsync(body.AsMemory(have, contentLength - have), readCts.Token);
if (n == 0) break;
have += n;View on GitHub (pinned to 1ced45e900)
Solutions
- Keep POST bodies under MaxSelectionBodyBytes.
- Send selections in smaller batches.
- Fix the client's Content-Length computation.
Defensive patterns
Strategy: validation
Validate before calling
// Client side: cap payload before posting
if (Encoding.UTF8.GetByteCount(bodyJson) > MaxSelectionBodyBytes)
throw new InvalidOperationException("selection payload too large; split into batches"); Try / catch
// Server side this is a hard refuse; client side avoid it: // ensure Content-Length == actual body length and stays under the cap.
Prevention
- Chunk large selection updates.
- Always set Content-Length to the true encoded body length.
- Never send negative Content-Length.
When it happens
Trigger: A client posts a selection/mark body with a Content-Length greater than MaxSelectionBodyBytes or a negative Content-Length to the watch server's selection endpoint.
Common situations: A buggy/automated client sending an oversized payload; a fuzzed/malicious request; a client computing Content-Length incorrectly.
Related errors
- body read timed out
- Refusing to fetch {what} from non-public address '{addr}' (h
- Remote {what} at {url} exceeds {max / (1024 * 1024)} MB limi
- max_depth_exceeded
- Invalid {contextKey} URL scheme '{scheme}:': only http, http
AI-assisted analysis of iOfficeAI/OfficeCLI@1ced45e900 (2026-08-13).
Data as JSON: /api/errors/ed63879f76918092.
Report an issue: GitHub.