{"id":"ccfbc5ab34f1ec0d","repo":"aio-libs/aiohttp","slug":"to-decode-nested-multipart-you-need-to-use-custom","errorCode":null,"errorMessage":"To decode nested multipart you need to use custom reader","messagePattern":"To decode nested multipart you need to use custom reader","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"aiohttp/web_request.py","lineNumber":802,"sourceCode":"                        raw_data = bytearray()\n                        while chunk := await field.read_chunk():\n                            size += len(chunk)\n                            if 0 < max_size < size:\n                                raise HTTPRequestEntityTooLarge(max_size)\n                            raw_data.extend(chunk)\n\n                        value = bytearray()\n                        # form-data doesn't support compression, so don't need to check size again.\n                        async for d in field.decode_iter(raw_data):  # type: ignore[arg-type]\n                            value.extend(d)\n\n                        if field_ct is None or field_ct.startswith(\"text/\"):\n                            charset = field.get_charset(default=\"utf-8\")\n                            out.add(field.name, value.decode(charset))\n                        else:\n                            out.add(field.name, value)  # type: ignore[arg-type]\n                else:\n                    raise ValueError(\n                        \"To decode nested multipart you need to use custom reader\",\n                    )\n        else:\n            data = await self.read()\n            if data:\n                charset = self.charset or \"utf-8\"\n                bytes_query = data.rstrip()\n                try:\n                    query = bytes_query.decode(charset)\n                except (LookupError, UnicodeDecodeError):\n                    raise HTTPUnsupportedMediaType()\n                out.extend(\n                    parse_qsl(qs=query, keep_blank_values=True, encoding=charset)\n                )\n\n        self._post = MultiDictProxy(out)\n        return self._post\n","sourceCodeStart":784,"sourceCodeEnd":820,"githubUrl":"https://github.com/aio-libs/aiohttp/blob/c0ef574e29109210e96e652771ae4e7b88615fa4/aiohttp/web_request.py#L784-L820","documentation":"Raised as ValueError by BaseRequest.post() (aiohttp/web_request.py:802) when the multipart parser encounters a nested multipart body part (a multipart/* part inside multipart/form-data) rather than a BodyPartReader. aiohttp's high-level post() helper does not attempt to recursively decode nested multipart; you must drive the MultipartReader yourself if your API legitimately needs nested multipart support.","triggerScenarios":"A request with Content-Type: multipart/form-data where one of the inner parts has its own Content-Type: multipart/mixed (or any multipart/* subtype). post()'s `if isinstance(field, BodyPartReader)` branch is the else, and the inner part is a MultipartReader -> ValueError.","commonSituations":"RFC 7578-style 'multiple files in one field' submitted via multipart/mixed; some older desktop uploaders and WebDAV-style clients; legacy APIs that wrapped several assets in a single form field.","solutions":["Use request.multipart() directly and recurse into nested MultipartReader instances yourself.","Change the client to submit each file as a separate top-level multipart/form-data part (filename=...).","Catch ValueError and return HTTPBadRequest explaining nested multipart is unsupported for that endpoint."],"exampleFix":"// before\nasync def handler(request):\n    form = await request.post()  # ValueError on nested multipart\n\n# after\nmultipart = await request.multipart()\nasync for part in multipart:\n    if isinstance(part, MultipartReader):\n        async for sub in part:\n            ...  # handle nested part manually","handlingStrategy":"try-catch","validationCode":"ct = request.content_type\nif ct and ct.startswith(\"multipart/\") and ct != \"multipart/form-data\":\n    raise web.HTTPBadRequest(text=\"nested multipart not supported\")","typeGuard":"def is_flat_multipart(request) -> bool:\n    return request.content_type == \"multipart/form-data\"","tryCatchPattern":"try:\n    form = await request.post()\nexcept ValueError as err:\n    if \"nested multipart\" in str(err):\n        raise web.HTTPBadRequest(text=\"use top-level form-data parts\")\n    raise","preventionTips":["Drive request.multipart() directly if you need nested multipart.","Tell clients to submit each file as a separate top-level part.","Distinguish this ValueError from others by message inspection or pre-validation of content type."],"tags":["request","multipart","nested","form-data"],"analyzedSha":"c0ef574e29109210e96e652771ae4e7b88615fa4","analyzedAt":"2026-08-04T19:51:05.467Z","schemaVersion":2}