aio-libs/aiohttp · error · ValueError
Could not find starting boundary {self._boundary!r}
Error message
Could not find starting boundary {self._boundary!r} What it means
Raised by MultipartReader._read_until_first_boundary() when the input stream returns empty (EOF) before the opening boundary line is found. This means the body is empty or the content does not contain the declared boundary at all, so the reader cannot locate the start of the first part.
Source
Thrown at aiohttp/multipart.py:850
)
def _get_boundary(self) -> str:
boundary = self._mimetype.parameters["boundary"]
if len(boundary) > 70:
raise ValueError("boundary %r is too long (70 chars max)" % boundary)
return boundary
async def _readline(self) -> bytes:
if self._unread:
return self._unread.pop()
return await self._content.readline()
async def _read_until_first_boundary(self) -> None:
while True:
chunk = await self._readline()
if chunk == b"":
raise ValueError(f"Could not find starting boundary {self._boundary!r}")
chunk = chunk.rstrip()
if chunk == self._boundary:
return
elif chunk == self._boundary + b"--":
self._at_eof = True
return
async def _read_boundary(self) -> None:
chunk = (await self._readline()).rstrip()
if chunk == self._boundary:
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 thenView on GitHub (pinned to c0ef574e29)
Solutions
- Verify the Content-Type boundary matches the delimiters actually present in the body.
- Check for an empty/truncated body upstream (Content-Length mismatch, early EOF).
- Catch ValueError around the first next() call and treat as a transport/protocol error with retry or 502.
Example fix
// before
part = await reader.next() # EOF before boundary -> ValueError
// after
try:
part = await reader.next()
except ValueError as e:
raise BadUpstream(f'multipart body missing boundary: {e}')
Defensive patterns
Strategy: try-catch
Validate before calling
first = await reader._content.peek(2) if hasattr(reader._content, 'peek') else None # generally not feasible to validate without consuming; rely on try/except
Try / catch
try:
part = await reader.next()
except ValueError as e:
if 'Could not find starting boundary' in str(e):
raise BadUpstream('empty or truncated multipart body')
raise Prevention
- Verify Content-Length and that the body is non-empty before parsing.
- Confirm the boundary in Content-Type matches the body delimiters.
- Handle early-EOF from upstream connections gracefully.
When it happens
Trigger: Calling `await reader.next()` (or iterating the reader) on a stream that ends before any line matching `--<boundary>` appears. Happens with empty bodies, truncated responses, or a boundary in Content-Type that does not match the actual body delimiters.
Common situations: Empty 200 response with a multipart Content-Type; proxy truncating the body; Content-Type boundary copied from a different message than the body; connection dropped mid-stream.
Related errors
- boundary missed for Content-Type: %s
- boundary %r is too long (70 chars max)
- Invalid boundary {chunk!r}, expected {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/477e3c8814937c2b.json.
Report an issue: GitHub.