{"record":{"id":"ed63879f76918092","repo":"iOfficeAI/OfficeCLI","slug":"body-too-large","errorCode":null,"errorMessage":"body too large","messagePattern":"body too large","errorType":"exception","errorClass":"InvalidDataException","httpStatus":null,"severity":"error","filePath":"src/officecli/Core/Watch/WatchServer.cs","lineNumber":2219,"sourceCode":"    /// straddles a read boundary into two independent fragments, and each\n    /// fragment decodes to U+FFFD — a large CJK payload came back from a\n    /// 200 OK with characters silently replaced, and the replacements went\n    /// straight into the document. Every /api POST shares this path so the\n    /// bound checks stay identical across endpoints.\n    ///\n    /// Bounded by MaxSelectionBodyBytes (FUZZER-001 slow-loris) and\n    /// PostBodyReadTimeout. A prefix overshooting Content-Length is trimmed:\n    /// otherwise extra bytes could be smuggled in the header segment\n    /// (FUZZER-002). A request with no Content-Length keeps the prefix as-is.\n    /// </summary>\n    private static async Task<string> ReadPostBodyAsync(\n        NetworkStream stream, Dictionary<string, string> headers, byte[] bodyPrefix, CancellationToken token)\n    {\n        int contentLength = -1;\n        if (headers.TryGetValue(\"Content-Length\", out var clStr) && int.TryParse(clStr, out var parsedCl))\n        {\n            if (parsedCl < 0 || parsedCl > MaxSelectionBodyBytes)\n                throw new InvalidDataException(\"body too large\");\n            contentLength = parsedCl;\n        }\n\n        if (contentLength < 0) return Encoding.UTF8.GetString(bodyPrefix);\n        if (bodyPrefix.Length >= contentLength) return Encoding.UTF8.GetString(bodyPrefix, 0, contentLength);\n\n        var body = new byte[contentLength];\n        bodyPrefix.CopyTo(body, 0);\n        int have = bodyPrefix.Length;\n        using var readCts = CancellationTokenSource.CreateLinkedTokenSource(token);\n        readCts.CancelAfter(PostBodyReadTimeout);\n        try\n        {\n            while (have < contentLength)\n            {\n                var n = await stream.ReadAsync(body.AsMemory(have, contentLength - have), readCts.Token);\n                if (n == 0) break;\n                have += n;","sourceCodeStart":2201,"sourceCodeEnd":2237,"githubUrl":"https://github.com/iOfficeAI/OfficeCLI/blob/1ced45e900782c5083ed550ddf328ee974e425e7/src/officecli/Core/Watch/WatchServer.cs#L2201-L2237","documentation":"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.","triggerScenarios":"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.","commonSituations":"A buggy/automated client sending an oversized payload; a fuzzed/malicious request; a client computing Content-Length incorrectly.","solutions":["Keep POST bodies under MaxSelectionBodyBytes.","Send selections in smaller batches.","Fix the client's Content-Length computation."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Client side: cap payload before posting\nif (Encoding.UTF8.GetByteCount(bodyJson) > MaxSelectionBodyBytes)\n    throw new InvalidOperationException(\"selection payload too large; split into batches\");","typeGuard":null,"tryCatchPattern":"// Server side this is a hard refuse; client side avoid it:\n// ensure Content-Length == actual body length and stays under the cap.","preventionTips":["Chunk large selection updates.","Always set Content-Length to the true encoded body length.","Never send negative Content-Length."],"tags":["watch","http","resource-limits","security","fuzzer"],"backgroundTag":null,"analyzedSha":"1ced45e900782c5083ed550ddf328ee974e425e7","analyzedAt":"2026-08-13T13:01:07.193Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}