aio-libs/aiohttp · error · ValueError

boundary %r is too long (70 chars max)

Error message

boundary %r is too long (70 chars max)

What it means

MultipartReader._get_boundary enforces the RFC 2046 limit of 70 characters on the boundary parameter. If the boundary string from the Content-Type exceeds 70 chars, ValueError is raised before any reading begins.

Solutions

  1. Shorten the boundary value to <= 70 characters (RFC 2046 allows up to 70).
  2. Use the boundary produced by MultipartWriter on the producing side, which respects the limit.
  3. Validate Content-Type boundary length on the server and return 400 before constructing the reader.
  4. Catch ValueError and reject the request as malformed.

Example fix

// before
Content-Type: multipart/form-data; boundary=AAAAAAAAAA...70+chars...AAAAA

// after
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxk
Defensive patterns

Strategy: validation

Validate before calling

MAX_BOUNDARY = 70

def assert_boundary_length(content_type: str) -> None:
    mt = parse_mimetype(content_type)
    b = mt.parameters.get('boundary')
    if mt.type == 'multipart' and (not b or len(b) > MAX_BOUNDARY):
        raise ValueError(f'boundary missing or too long (> {MAX_BOUNDARY})')

Type guard

def is_valid_boundary_length(content_type: object) -> bool:
    if not isinstance(content_type, str):
        return False
    mt = parse_mimetype(content_type)
    b = mt.parameters.get('boundary')
    return mt.type != 'multipart' or bool(b) and len(b) <= 70

Try / catch

try:
    reader = MultipartReader(headers, content)
except ValueError as e:
    if 'too long' in str(e):
        return web.Response(status=400, text='Boundary exceeds 70 characters')
    raise

Prevention

When it happens

Trigger: A Content-Type header whose boundary= value is longer than 70 characters; typically a misconfigured or adversarial sender.

Common situations: Producers generating boundaries from long UUIDs concatenated with extra prefixes/suffixes; copy-paste of base64 blobs as boundary; fuzzing.

Related errors


AI-assisted analysis of aio-libs/aiohttp@d041d4d0fd (2026-08-11). Data as JSON: /api/errors/1880818c3e2a2de5. Report an issue: GitHub.

Appendix: source

Thrown at aiohttp/multipart.py:841

                max_field_size=self._max_field_size,
                max_headers=self._max_headers,
                max_size_error_cls=self._max_size_error_cls,
            )
        else:
            return self.part_reader_cls(
                self._boundary,
                headers,
                self._content,
                subtype=self._mimetype.subtype,
                default_charset=self._default_charset,
                client_max_size=self._client_max_size,
                max_size_error_cls=self._max_size_error_cls,
            )

    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

View on GitHub (pinned to d041d4d0fd)