aio-libs/aiohttp · error · TypeError
Cannot create payload from %r
Error message
Cannot create payload from %r
What it means
MultipartWriter.append delegates to get_payload(obj) to convert a non-Payload object into a Payload. If no payload factory is registered for the object's type, get_payload raises LookupError, which append converts to TypeError 'Cannot create payload from %r'.
Solutions
- Convert the object to a supported primitive first: mw.append(JsonPayload({'k': 'v'})).
- Wrap bytes/str/file in the matching payload (BytesPayload, StringPayload, BufferedReaderPayload).
- Register a payload factory for your type with payload.register_payload_factory(MyType, factory).
- If appending JSON, use aiohttp.payload.JsonPayload(obj).
Example fix
// before
mw.append({'name': 'alice'})
// after
from aiohttp import JsonPayload
mw.append(JsonPayload({'name': 'alice'})) Defensive patterns
Strategy: type-guard
Validate before calling
from aiohttp.payload import PAYLOAD_REGISTRY, JsonPayload, BytesPayload, StringPayload
import json, typing
def to_payload(obj, headers=None):
if isinstance(obj, (bytes, bytearray)):
return BytesPayload(bytes(obj), headers=headers)
if isinstance(obj, str):
return StringPayload(obj, headers=headers)
if isinstance(obj, (dict, list)):
return JsonPayload(obj, headers=headers)
# last resort: try the registry
from aiohttp.payload import get_payload
return get_payload(obj, headers=headers) Type guard
from aiohttp.payload import Payload
import typing
def is_payload_or_supported(obj: typing.Any) -> bool:
if isinstance(obj, Payload):
return True
return isinstance(obj, (bytes, bytearray, str, dict, list)) Try / catch
try:
mw.append(obj)
except TypeError as e:
if 'Cannot create payload' in str(e):
mw.append(JsonPayload(json.dumps(obj)))
else:
raise Prevention
- Convert custom objects to bytes/str/JSON before appending, or wrap them in a Payload subclass.
- Register payload factories for your own types via aiohttp.payload.register_payload_factory.
- Prefer JsonPayload for dict/list, BytesPayload for bytes, StringPayload for str.
When it happens
Trigger: Calling mw.append(obj) with an object type aiohttp has no payload factory for: arbitrary custom classes, nested dicts/lists (without JsonPayload), numpy arrays, dataclasses, SQLAlchemy models, None (in some contexts), etc.
Common situations: Appending a dict directly instead of json.dumps-ing it; appending a custom dataclass; appending an object whose type was never registered via payload_type/register payload factories.
Related errors
- Can not serialize value type: %r headers: %r value: %r
- Unable to decode.
- Unable to read body part as bytes. Use write() to consume.
- boundary should contain ASCII only chars
- boundary value contains invalid characters
AI-assisted analysis of aio-libs/aiohttp@d041d4d0fd (2026-08-11).
Data as JSON: /api/errors/6c0f73863974ab77.
Report an issue: GitHub.
Appendix: source
Thrown at aiohttp/multipart.py:1015
return '"' + quoted_value_content.decode("ascii") + '"'
@property
def boundary(self) -> str:
return self._boundary.decode("ascii")
def append(self, obj: Any, headers: Mapping[str, str] | None = None) -> Payload:
if headers is None:
headers = CIMultiDict()
if isinstance(obj, Payload):
obj.headers.update(headers)
return self.append_payload(obj)
else:
try:
payload = get_payload(obj, headers=headers)
except LookupError:
raise TypeError("Cannot create payload from %r" % obj)
else:
return self.append_payload(payload)
def append_payload(self, payload: Payload) -> Payload:
"""Adds a new body part to multipart writer."""
encoding: str | None = None
te_encoding: str | None = None
if self._is_form_data:
# https://datatracker.ietf.org/doc/html/rfc7578#section-4.7
# https://datatracker.ietf.org/doc/html/rfc7578#section-4.8
assert (
not {CONTENT_ENCODING, CONTENT_LENGTH, CONTENT_TRANSFER_ENCODING}
& payload.headers.keys()
)
# Set default Content-Disposition in case user doesn't create one
if CONTENT_DISPOSITION not in payload.headers:
name = f"section-{len(self._parts)}"
payload.set_content_disposition("form-data", name=name)View on GitHub (pinned to d041d4d0fd)