{"id":"e625148e4230eed6","repo":"aio-libs/aiohttp","slug":"invalid-content-length-length-r","errorCode":null,"errorMessage":"invalid Content-Length: {length!r}","messagePattern":"invalid Content-Length: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"aiohttp/multipart.py","lineNumber":304,"sourceCode":"        default_charset: str | None = None,\n        max_decompress_size: int = DEFAULT_CHUNK_SIZE,\n        client_max_size: int = sys.maxsize,\n        max_size_error_cls: type[Exception] = ValueError,\n    ) -> None:\n        self.headers = headers\n        self._boundary = boundary\n        self._boundary_len = len(boundary) + 2  # Boundary + \\r\\n\n        self._content = content\n        self._default_charset = default_charset\n        self._at_eof = False\n        self._is_form_data = subtype == \"form-data\"\n        # https://datatracker.ietf.org/doc/html/rfc7578#section-4.8\n        length = None if self._is_form_data else self.headers.get(CONTENT_LENGTH, None)\n        if length is not None and not (length.isascii() and length.isdigit()):\n            # Reject sign prefixes, underscores, whitespace and non-ASCII\n            # digits that int() would otherwise accept.\n            # https://www.rfc-editor.org/rfc/rfc9110#section-8.6\n            raise ValueError(f\"invalid Content-Length: {length!r}\")\n        self._length = int(length) if length is not None else None\n        self._read_bytes = 0\n        self._unread: deque[bytes] = deque()\n        self._prev_chunk: bytes | None = None\n        self._content_eof = 0\n        self._cache: dict[str, Any] = {}\n        self._max_decompress_size = max_decompress_size\n        self._client_max_size = client_max_size\n        self._max_size_error_cls = max_size_error_cls\n\n    def __aiter__(self) -> Self:\n        return self\n\n    async def __anext__(self) -> bytes:\n        part = await self.next()\n        if part is None:\n            raise StopAsyncIteration\n        return part","sourceCodeStart":286,"sourceCodeEnd":322,"githubUrl":"https://github.com/aio-libs/aiohttp/blob/c0ef574e29109210e96e652771ae4e7b88615fa4/aiohttp/multipart.py#L286-L322","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Validate the multipart payload source (browser/encoder) sets a plain-digit Content-Length.","Sanitise/normalise part Content-Length before constructing BodyPartReader if you control framing.","Catch ValueError when iterating parts and return 400."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"def valid_part_length(length: str | None) -> bool:\n    return length is None or (length.isascii() and length.isdigit())","typeGuard":"def valid_part_length(length: str | None) -> bool:\n    return length is None or (length.isascii() and length.isdigit())","tryCatchPattern":"try:\n    async for part in multipart_reader:\n        ...\nexcept ValueError:\n    return web.Response(status=400, text='invalid part Content-Length')","preventionTips":["Ensure multipart part Content-Length is plain digits.","Validate uploaded multipart at the edge."],"tags":["multipart","content-length","validation","parser"],"analyzedSha":"c0ef574e29109210e96e652771ae4e7b88615fa4","analyzedAt":"2026-08-04T19:51:05.467Z","schemaVersion":2}