iOfficeAI/OfficeCLI · error · InvalidDataException
body read timed out
Error message
body read timed out
What it means
Thrown by ReadPostBodyAsync when reading the declared Content-Length bytes does not complete within PostBodyReadTimeout (a linked CancellationToken cancels and, when the external token is not the cause, converts OperationCanceledException into InvalidDataException 'body read timed out'). It stops a slow client from holding a connection open (slow-loris) after sending a large Content-Length.
Source
Thrown at src/officecli/Core/Watch/WatchServer.cs:2242
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;
}
}
catch (OperationCanceledException) when (!token.IsCancellationRequested)
{
throw new InvalidDataException("body read timed out");
}
return Encoding.UTF8.GetString(body, 0, have);
}
private async Task HandlePostSelectionAsync(NetworkStream stream, Dictionary<string, string> headers, byte[] bodyPrefix, CancellationToken token)
{
int statusCode = 204;
string statusText = "No Content";
try
{
var body = await ReadPostBodyAsync(stream, headers, bodyPrefix, token);
// Expected JSON: {"paths": ["/slide[1]/shape[2]", ...]}
var req = JsonSerializer.Deserialize(body, WatchSelectionJsonContext.Default.SelectionRequest);
var rawSelection = req?.Paths ?? new List<string>();
// BUG-TESTER-R501/R502 + BUG-FUZZER-R5-04: bring selection path
// hardening up to parity with mark (Round 2/3 fixes). Each path isView on GitHub (pinned to 1ced45e900)
Solutions
- Send the entire body promptly after headers.
- Reduce payload size so it transmits within the timeout.
- Reconnect and retry the POST if the connection dropped.
Defensive patterns
Strategy: retry
Validate before calling
// Client side: write the full body in one shot and ensure it matches Content-Length var bytes = System.Text.Encoding.UTF8.GetBytes(bodyJson); request.ContentLength = bytes.Length; // must equal actual length await stream.WriteAsync(bytes);
Try / catch
// On the client, treat a timed-out POST as retryable:
for (int i = 0; i < 2; i++)
try { await PostSelectionAsync(body); return; }
catch (System.IO.InvalidDataException) when (i < 1) { /* reconnect and retry */ } Prevention
- Flush the whole body immediately after headers.
- Keep payloads small enough to fit the read timeout.
- Reconnect on a stalled write rather than leaving it half-sent.
When it happens
Trigger: A client declares a large Content-Length but sends bytes slowly or stalls mid-body so the read loop cannot finish within PostBodyReadTimeout; a dropped connection mid-upload.
Common situations: Flaky/slow network to the watch client; a client that crashes after sending headers; a deliberate slow-loris probe.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- body too large
- Refusing to fetch {what} from non-public address '{addr}' (h
- Pipe communication timed out
- render timed out after 120s
- -1
AI-assisted analysis of iOfficeAI/OfficeCLI@1ced45e900 (2026-08-13).
Data as JSON: /api/errors/848cffdb3e06da51.
Report an issue: GitHub.