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 then

View on GitHub (pinned to c0ef574e29)

Solutions

  1. Verify the Content-Type boundary matches the delimiters actually present in the body.
  2. Check for an empty/truncated body upstream (Content-Length mismatch, early EOF).
  3. 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

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


AI-assisted analysis of aio-libs/aiohttp@c0ef574e29 (2026-08-04). Data as JSON: /data/errors/477e3c8814937c2b.json. Report an issue: GitHub.