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
BaseRequest.post() only handles a single level of multipart/form-data. If a part is itself a MultipartReader (nested multipart, e.g. multipart/mixed or message/rfc822 inside form-data), it raises ValueError directing you to use a custom reader. post() will not recurse into nested multipart.
Solutions
- Use request.multipart() directly and walk the reader yourself to handle nested parts.
- Do not call post() for nested multipart; write a dedicated parser.
- Reject nested multipart with 400 if your API does not support it.
Example fix
# before
data = await request.post() # raises on nested multipart
# after
reader = await request.multipart()
async for part in reader:
if isinstance(part, MultipartReader):
# handle nested multipart manually
... Defensive patterns
Strategy: validation
Validate before calling
if request.content_type == 'multipart/form-data':
# peek for nested multipart before calling post()
reader = await request.multipart()
# handle nested parts manually Type guard
def needs_custom_reader(part: object) -> bool:
return not isinstance(part, BodyPartReader) Try / catch
try:
data = await request.post()
except ValueError:
# fall back to a custom multipart reader
reader = await request.multipart() Prevention
- Use request.multipart() directly when nested parts are possible.
- Reject nested multipart with 400 if your API does not support it.
- Document that post() supports only flat multipart/form-data.
When it happens
Trigger: A multipart/form-data body containing a nested multipart/* part (aggregate payloads, email-style submissions, RFC 7578 nested examples).
Common situations: Clients composing multiple files under one field with multipart/mixed; email gateways; aggregate document uploads.
Related errors
- Can not serialize value type: %r headers: %r value: %r
- content_type must be an instance of str. Got
- data cannot be decoded with
- filename must be an instance of str. Got
- Invalid default charset
AI-assisted analysis of aio-libs/aiohttp@d041d4d0fd (2026-08-11).
Data as JSON: /api/errors/ccfbc5ab34f1ec0d.
Report an issue: GitHub.
Appendix: 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 d041d4d0fd)