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

  1. Convert the object to a supported primitive first: mw.append(JsonPayload({'k': 'v'})).
  2. Wrap bytes/str/file in the matching payload (BytesPayload, StringPayload, BufferedReaderPayload).
  3. Register a payload factory for your type with payload.register_payload_factory(MyType, factory).
  4. 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

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


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)