aio-libs/aiohttp · error · ValueError
To decode nested multipart you need to use custom reader
Error message
To decode nested multipart you need to use custom reader
What it means
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.
Source
Thrown at aiohttp/web_request.py:802
raw_data = bytearray()
while chunk := await field.read_chunk():
size += len(chunk)
if 0 < max_size < size:
raise HTTPRequestEntityTooLarge(max_size)
raw_data.extend(chunk)
value = bytearray()
# form-data doesn't support compression, so don't need to check size again.
async for d in field.decode_iter(raw_data): # type: ignore[arg-type]
value.extend(d)
if field_ct is None or field_ct.startswith("text/"):
charset = field.get_charset(default="utf-8")
out.add(field.name, value.decode(charset))
else:
out.add(field.name, value) # type: ignore[arg-type]
else:
raise ValueError(
"To decode nested multipart you need to use custom reader",
)
else:
data = await self.read()
if data:
charset = self.charset or "utf-8"
bytes_query = data.rstrip()
try:
query = bytes_query.decode(charset)
except (LookupError, UnicodeDecodeError):
raise HTTPUnsupportedMediaType()
out.extend(
parse_qsl(qs=query, keep_blank_values=True, encoding=charset)
)
self._post = MultiDictProxy(out)
return self._post
View on GitHub (pinned to c0ef574e29)
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.
Example fix
// before
async def handler(request):
form = await request.post() # ValueError on nested multipart
# after
multipart = await request.multipart()
async for part in multipart:
if isinstance(part, MultipartReader):
async for sub in part:
... # handle nested part manually Defensive patterns
Strategy: try-catch
Validate before calling
ct = request.content_type
if ct and ct.startswith("multipart/") and ct != "multipart/form-data":
raise web.HTTPBadRequest(text="nested multipart not supported") Type guard
def is_flat_multipart(request) -> bool:
return request.content_type == "multipart/form-data" Try / catch
try:
form = await request.post()
except ValueError as err:
if "nested multipart" in str(err):
raise web.HTTPBadRequest(text="use top-level form-data parts")
raise Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Multipart field missing name.
- data cannot be decoded with %s encoding
- Invalid default charset
- filename must be an instance of str. Got: %s
- content_type must be an instance of str. Got: %s
AI-assisted analysis of aio-libs/aiohttp@c0ef574e29 (2026-08-04).
Data as JSON: /data/errors/ccfbc5ab34f1ec0d.json.
Report an issue: GitHub.