aio-libs/aiohttp · error · ValueError

boundary missed for Content-Type: %s

Error message

boundary missed for Content-Type: %s

What it means

Raised by MultipartReader.__init__ when the Content-Type header indicates a multipart/* type but lacks the required 'boundary' parameter. Per RFC 2046 §5.1.1 the boundary delimiter is mandatory for multipart bodies, so the reader cannot segment the stream without it.

Source

Thrown at aiohttp/multipart.py:695

    #: None points to type(self)
    multipart_reader_cls: type["MultipartReader"] | None = None
    #: Body part reader class for non multipart/* content types.
    part_reader_cls = BodyPartReader

    def __init__(
        self,
        headers: Mapping[str, str],
        content: StreamReader,
        *,
        client_max_size: int = sys.maxsize,
        max_field_size: int = 8190,
        max_headers: int = 128,
        max_size_error_cls: type[Exception] = ValueError,
    ) -> None:
        self._mimetype = parse_mimetype(headers[CONTENT_TYPE])
        assert self._mimetype.type == "multipart", "multipart/* content type expected"
        if "boundary" not in self._mimetype.parameters:
            raise ValueError(
                "boundary missed for Content-Type: %s" % headers[CONTENT_TYPE]
            )

        self.headers = headers
        self._boundary = ("--" + self._get_boundary()).encode()
        self._client_max_size = client_max_size
        self._content = content
        self._default_charset: str | None = None
        self._last_part: MultipartReader | BodyPartReader | None = None
        self._max_field_size = max_field_size
        self._max_headers = max_headers
        self._max_size_error_cls = max_size_error_cls
        self._at_eof = False
        self._at_bof = True
        self._unread: list[bytes] = []

    def __aiter__(self) -> Self:
        return self

View on GitHub (pinned to c0ef574e29)

Solutions

  1. Ensure the sender includes a valid boundary in the Content-Type header.
  2. If you control the response, use MultipartWriter which generates the boundary automatically.
  3. Validate the Content-Type header before calling multipart() and reject with a clear error.

Example fix

// before
reader = await response.multipart()  # no boundary -> ValueError
// after
if 'boundary=' not in response.headers.get('Content-Type', ''):
    raise ValueError('upstream omitted multipart boundary')
reader = await response.multipart()
Defensive patterns

Strategy: validation

Validate before calling

ctype = response.headers.get('Content-Type', '')
if 'multipart/' in ctype and 'boundary=' not in ctype:
    raise ValueError('Content-Type missing boundary parameter')

Type guard

import re

def has_boundary(content_type: str) -> bool:
    return bool(re.search(r'boundary=', content_type, re.I))

Try / catch

try:
    reader = await response.multipart()
except ValueError as e:
    # missing boundary — reject upstream
    raise BadUpstream(str(e))

Prevention

When it happens

Trigger: Constructing a MultipartReader (directly or via `response.multipart()` / `request.multipart()`) where the Content-Type is e.g. 'multipart/form-data' with no `; boundary=...`. The assert for multipart type passes but the boundary check fails.

Common situations: Upstream sends `Content-Type: multipart/form-data` and forgets the boundary (bug); a proxy strips the boundary parameter; manually crafted responses/requests; Content-Type header truncated by a size limit.

Related errors


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