aio-libs/aiohttp · error · ValueError

invalid Content-Length: {length!r}

Error message

invalid Content-Length: {length!r}

What it means

ValueError raised by BodyPartReader.__init__ when a (non-form-data) multipart body part's Content-Length is present but not purely ASCII digits. aiohttp rejects signs, whitespace, underscores, and non-ASCII digits that int() would otherwise accept, per RFC 9110 section 8.6.

Source

Thrown at aiohttp/multipart.py:304

        default_charset: str | None = None,
        max_decompress_size: int = DEFAULT_CHUNK_SIZE,
        client_max_size: int = sys.maxsize,
        max_size_error_cls: type[Exception] = ValueError,
    ) -> None:
        self.headers = headers
        self._boundary = boundary
        self._boundary_len = len(boundary) + 2  # Boundary + \r\n
        self._content = content
        self._default_charset = default_charset
        self._at_eof = False
        self._is_form_data = subtype == "form-data"
        # https://datatracker.ietf.org/doc/html/rfc7578#section-4.8
        length = None if self._is_form_data else self.headers.get(CONTENT_LENGTH, None)
        if length is not None and not (length.isascii() and length.isdigit()):
            # Reject sign prefixes, underscores, whitespace and non-ASCII
            # digits that int() would otherwise accept.
            # https://www.rfc-editor.org/rfc/rfc9110#section-8.6
            raise ValueError(f"invalid Content-Length: {length!r}")
        self._length = int(length) if length is not None else None
        self._read_bytes = 0
        self._unread: deque[bytes] = deque()
        self._prev_chunk: bytes | None = None
        self._content_eof = 0
        self._cache: dict[str, Any] = {}
        self._max_decompress_size = max_decompress_size
        self._client_max_size = client_max_size
        self._max_size_error_cls = max_size_error_cls

    def __aiter__(self) -> Self:
        return self

    async def __anext__(self) -> bytes:
        part = await self.next()
        if part is None:
            raise StopAsyncIteration
        return part

View on GitHub (pinned to c0ef574e29)

Solutions

  1. Validate the multipart payload source (browser/encoder) sets a plain-digit Content-Length.
  2. Sanitise/normalise part Content-Length before constructing BodyPartReader if you control framing.
  3. Catch ValueError when iterating parts and return 400.
Defensive patterns

Strategy: validation

Validate before calling

def valid_part_length(length: str | None) -> bool:
    return length is None or (length.isascii() and length.isdigit())

Type guard

def valid_part_length(length: str | None) -> bool:
    return length is None or (length.isascii() and length.isdigit())

Try / catch

try:
    async for part in multipart_reader:
        ...
except ValueError:
    return web.Response(status=400, text='invalid part Content-Length')

Prevention

When it happens

Trigger: Reading a multipart payload where a part carries a Content-Length like '+10', '1_000', ' 10 ', or non-ASCII digits. The constructor checks `length.isascii() and length.isdigit()` and raises ValueError before parsing.

Common situations: Malformed multipart payloads from a buggy uploader; crafted input probing int() leniency; a part header injected with whitespace/sign; custom client setting Content-Length incorrectly.

Related errors


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