aio-libs/aiohttp · error · ValueError
Reader did not read all the data or it is malformed
Error message
Reader did not read all the data or it is malformed
What it means
Raised at end-of-stream when the body part's trailing CRLF is missing or wrong. After reading exactly _length bytes, BodyPartReader expects the next two bytes read via readline() to equal b'\r\n'; anything else means the declared Content-Length did not match the actual body, or the framing CRLF before the boundary is absent.
Solutions
- Verify the producer writes exactly Content-Length bytes followed by '\r\n' before the next boundary.
- If Content-Length is uncertain, emit the part without it and rely on boundary framing instead.
- Re-encode transfer-encoding (base64/QP) consistently on both length computation and body bytes.
- Catch ValueError at the call site of read_chunk()/read() to tolerate truncated streams and log the offending part.
Example fix
// before --BOUNDARY Content-Length: 5 hello--BOUNDARY-- // after --BOUNDARY Content-Length: 5 hello --BOUNDARY--
Defensive patterns
Strategy: try-catch
Try / catch
try:
while not part.at_eof():
chunk = await part.read_chunk(8192)
except ValueError as e:
# part body did not match Content-Length or lacked trailing CRLF
log.warning('Malformed body part %s: %s', part.name, e)
raise web.HTTPBadRequest(text='Malformed multipart body') Prevention
- When generating multipart, write exactly Content-Length bytes then a CRLF before the boundary.
- Compute Content-Length from the same bytes you actually write (post transfer-encoding).
- Treat read failures as fatal framing errors; do not attempt partial processing.
When it happens
Trigger: A body part whose Content-Length under- or over-counts the real payload bytes, or a producer that omits the CRLF separating part body from the boundary line. Also triggered when the stream is truncated mid-part.
Common situations: Multipart generators that compute Content-Length from the undecoded size but the reader decodes base64/quoted-printable, or vice versa; truncated responses from a closing proxy; tests with hand-written multipart strings missing the trailing CRLF.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Could not find starting boundary
- Invalid boundary , expected
- invalid Content-Length
- Reading after EOF
- boundary missed for Content-Type
AI-assisted analysis of aio-libs/aiohttp@d041d4d0fd (2026-08-11).
Data as JSON: /api/errors/ec50413820657ea7.
Report an issue: GitHub.
Appendix: source
Thrown at aiohttp/multipart.py:395
if self._prev_chunk:
over_chunk = self._prev_chunk[:over_chunk_size]
self._prev_chunk = self._prev_chunk[len(over_chunk) :]
if len(over_chunk) != over_chunk_size:
over_chunk += await self._content.read(4 - len(over_chunk))
if not over_chunk:
self._at_eof = True
stripped_chunk += b"".join(over_chunk.split())
chunk += over_chunk
remainder = len(stripped_chunk) % 4
self._read_bytes += len(chunk)
if self._read_bytes == self._length:
self._at_eof = True
if self._at_eof and await self._content.readline() != b"\r\n":
raise ValueError("Reader did not read all the data or it is malformed")
return chunk
async def _read_chunk_from_length(self, size: int) -> bytes:
# Reads body part content chunk of the specified size.
# The body part must has Content-Length header with proper value.
assert self._length is not None, "Content-Length required for chunked read"
chunk_size = min(size, self._length - self._read_bytes)
chunk = await self._content.read(chunk_size)
if self._content.at_eof():
self._at_eof = True
return chunk
async def _read_chunk_from_stream(self, size: int) -> bytes:
# Reads content chunk of body part with unknown length.
# The Content-Length header for body part is not necessary.
assert (
size >= self._boundary_len
), "Chunk size must be greater or equal than boundary length + 2"View on GitHub (pinned to d041d4d0fd)