aio-libs/aiohttp · error · TypeError

Cannot create payload from %r

Error message

Cannot create payload from %r

What it means

Raised by MultipartWriter.append() when get_payload() cannot find any registered Payload factory for the given object type (it raises LookupError, which append converts to TypeError). The payload registry only knows a fixed set of types (bytes, str, IO, JSON-serializable, etc.); anything else cannot be turned into a body part.

Source

Thrown at aiohttp/multipart.py:1006

        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 c0ef574e29)

Solutions

  1. Convert the object to a supported type first: `writer.append_json(obj)`, or `writer.append(str(obj))`, or `writer.append(bytes(obj))`.
  2. Wrap it in an explicit Payload subclass (e.g. JsonPayload, BytesPayload) and pass that.
  3. Register a custom payload factory with @payload_type for your class if you append it frequently.

Example fix

// before
writer.append(42)  # int has no factory -> TypeError
// after
writer.append_json({'value': 42})
Defensive patterns

Strategy: type-guard

Validate before calling

from aiohttp.payload import PAYLOAD_REGISTRY_FACTORY_TYPES  # conceptual
supported = (bytes, bytearray, memoryview, str, dict, list, tuple, io.IOBase)
if not isinstance(obj, supported):
    raise TypeError(f'no payload factory for {type(obj)}')

Type guard

def has_payload_factory(obj) -> bool:
    from aiohttp.payload import get_payload
    try:
        get_payload(obj)
        return True
    except LookupError:
        return False

Try / catch

try:
    writer.append(obj)
except TypeError:
    writer.append_json(obj)  # or str(obj) / bytes(obj)

Prevention

When it happens

Trigger: Calling `writer.append(obj)` where obj is a type with no registered payload factory — e.g. an int, a custom dataclass instance, a dict passed where a JSON/Bytes payload was expected, or None.

Common situations: Appending a bare number, a non-serializable object, or a custom class without first converting it to bytes/str/JSON; forgetting to use append_json() for dict/list data; passing a Payload subclass that isn't registered.

Related errors


AI-assisted analysis of aio-libs/aiohttp@c0ef574e29 (2026-08-04). Data as JSON: /data/errors/6c0f73863974ab77.json. Report an issue: GitHub.