ThreeMammals/Ocelot · error · InvalidOperationException
Sent request content bytes, but Content-Length promised .
Error message
Sent {contentLength} request content bytes, but Content-Length promised {announcedContentLength}. What it means
The complementary check in StreamHttpContent.CopyAsync (called through SerializeToStreamAsync while Ocelot forwards the request body): after the input stream is exhausted, the total bytes sent (contentLength) is less than announcedContentLength, the Content-Length the client promised. The comment in the source notes this mismatch normally would be caught by the server, but a component in the proxy chain may have truncated the body (e.g. swallowed the stream tail, closed early, or transformed content) without adjusting Content-Length. CopyAsync throws InvalidOperationException so the incomplete request is not silently forwarded to the downstream service, which would otherwise hang or misparse the body waiting for the missing bytes. The input at fault is the request body stream whose actual length diverges from its declared Content-Length.
Solutions
- Locate the proxy-stage component (middleware, handler, mapper) that truncated or transformed the body and fix it to emit the full byte range.
- Recompute or remove the Content-Length header whenever the body is transformed, or use chunked encoding instead.
- Enable request-body logging/buffering temporarily to compare the raw incoming body size against what reaches StreamHttpContent.
- Check for early disposal or cancellation of the incoming stream (client aborts, timeouts) that ends CopyAsync before all bytes are read.
- At the call site, catch the InvalidOperationException and translate it into a 400/502 response rather than forwarding a truncated body downstream.
Defensive patterns
Strategy: validation
When it happens
Trigger: Thrown at src/Request/Mapper/StreamHttpContent.cs:93 when the library encounters an invalid state.
Common situations: See trigger scenarios.
AI-assisted analysis of ThreeMammals/Ocelot@d1f22d9304 (2026-09-12).
Data as JSON: /api/errors/310e1361895bd013.
Report an issue: GitHub.
Appendix: source
Thrown at src/Request/Mapper/StreamHttpContent.cs:93
var read = await input.ReadAsync(buffer.AsMemory(), cancellation);
contentLength += read;
// Normally this is enforced by the server, but it could get out of sync if something in the proxy modified the body.
if (announcedContentLength != UnknownLength && contentLength > announcedContentLength)
{
throw new InvalidOperationException($"More data ({contentLength} bytes) received than the specified Content-Length of {announcedContentLength} bytes.");
}
// End of the source stream.
if (read == 0)
{
if (announcedContentLength == UnknownLength || contentLength == announcedContentLength)
{
return;
}
else
{
throw new InvalidOperationException($"Sent {contentLength} request content bytes, but Content-Length promised {announcedContentLength}.");
}
}
await output.WriteAsync(buffer.AsMemory(0, read), cancellation);
if (autoFlush)
{
// HttpClient doesn't always flush outgoing data unless the buffer is full or the caller asks.
// This is a problem for streaming protocols like WebSockets and gRPC.
await output.FlushAsync(cancellation);
}
}
}
finally
{
if (buffer != null)
{
ArrayPool<byte>.Shared.Return(buffer);
}
View on GitHub (pinned to d1f22d9304)