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

  1. Keep POST bodies under MaxSelectionBodyBytes.
  2. Send selections in smaller batches.
  3. 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

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


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