aio-libs/aiohttp · error · ValueError
Invalid boundary {chunk!r}, expected {self._boundary!r}
Error message
Invalid boundary {chunk!r}, expected {self._boundary!r} What it means
Raised by MultipartReader._read_boundary() when, after the first part, the reader reads a delimiter line that is neither the expected `--<boundary>` (start of next part) nor `--<boundary>--` (final delimiter). This indicates the stream is corrupted or the boundary has drifted out of sync.
Source
Thrown at aiohttp/multipart.py:879
pass
elif chunk == self._boundary + b"--":
self._at_eof = True
epilogue = await self._readline()
next_line = await self._readline()
# the epilogue is expected and then either the end of input or the
# parent multipart boundary, if the parent boundary is found then
# it should be marked as unread and handed to the parent for
# processing
if next_line[:2] == b"--":
self._unread.append(next_line)
# otherwise the request is likely missing an epilogue and both
# lines should be passed to the parent for processing
# (this handles the old behavior gracefully)
else:
self._unread.extend([next_line, epilogue])
else:
raise ValueError(f"Invalid boundary {chunk!r}, expected {self._boundary!r}")
async def _read_headers(self) -> HeadersDictProxy:
lines = []
while True:
chunk = await self._content.readline(max_line_length=self._max_field_size)
chunk = chunk.rstrip(b"\r\n")
lines.append(chunk)
if not chunk:
break
if len(lines) > self._max_headers:
raise BadHttpMessage("Too many headers received")
parser = HeadersParser(max_field_size=self._max_field_size)
headers, _ = parser.parse_headers(lines)
return headers
async def _maybe_release_last_part(self) -> None:
"""Ensures that the last read body part is read completely."""
if self._last_part is not None:View on GitHub (pinned to c0ef574e29)
Solutions
- Confirm the Content-Type boundary exactly equals the delimiters inside the body.
- Ensure upstream sets correct Content-Length on each part or uses chunked framing consistently.
- Catch ValueError and abort the multipart parse with a 400/502.
Example fix
// before
async for part in reader: # mid-body delimiter mismatch -> ValueError
...
// after
try:
async for part in reader:
...
except ValueError as e:
return web.Response(status=400, text=f'malformed multipart: {e}')
Defensive patterns
Strategy: try-catch
Try / catch
try:
async for part in reader:
handle(part)
except ValueError as e:
if 'Invalid boundary' in str(e):
return web.Response(status=400, text='multipart stream corrupted')
raise Prevention
- Ensure each part has a correct Content-Length so boundaries stay aligned.
- Do not manually edit a multipart body without re-deriving boundaries.
- Treat mid-stream boundary mismatch as unrecoverable corruption.
When it happens
Trigger: Iterating parts with `await reader.next()` and the bytes between parts do not match the configured boundary. Caused by a body whose internal delimiters differ from the Content-Type boundary, or by part bodies that leaked across the boundary due to a wrong Content-Length.
Common situations: Mismatched boundary between Content-Type and body; a part with an incorrect Content-Length causing the reader to start mid-part; nested multipart with wrong reader; man-in-the-middle corruption.
Related errors
- boundary missed for Content-Type: %s
- boundary %r is too long (70 chars max)
- Could not find starting boundary {self._boundary!r}
- Reader did not read all the data or it is malformed
- Reading after EOF
AI-assisted analysis of aio-libs/aiohttp@c0ef574e29 (2026-08-04).
Data as JSON: /data/errors/c54332184ce8e026.json.
Report an issue: GitHub.