aio-libs/aiohttp · error · ValueError

Unsupported body type %r

Error message

Unsupported body type %r

What it means

The body setter tries to coerce non-bytes values via payload.PAYLOAD_REGISTRY.get (line 618). If no registered Payload type matches the object, it raises ValueError listing the unsupported type. Only bytes/bytearray pass directly; everything else needs a registered payload type (e.g. str, dict via aiohttp payload types).

Source

Thrown at aiohttp/web_response.py:620

        self._zlib_executor_size = zlib_executor_size
        self._zlib_executor = zlib_executor

    @property
    def body(self) -> bytes | bytearray | Payload | None:
        return self._body

    @body.setter
    def body(self, body: Any) -> None:
        if body is None:
            self._body = None
        elif isinstance(body, (bytes, bytearray)):
            self._body = body
        else:
            try:
                self._body = body = payload.PAYLOAD_REGISTRY.get(body)
            except payload.LookupError:
                raise ValueError("Unsupported body type %r" % type(body))

            headers = self._headers

            # set content-type
            if hdrs.CONTENT_TYPE not in headers:
                headers[hdrs.CONTENT_TYPE] = body.content_type

            # copy payload headers
            if body.headers:
                for key, value in body.headers.items():
                    if key not in headers:
                        headers[key] = value

        self._compressed_body = None

    @property
    def text(self) -> str | None:
        if self._body is None:

View on GitHub (pinned to c0ef574e29)

Solutions

  1. Encode to bytes first: resp.body = str(obj).encode() or json.dumps(obj).encode().
  2. For str use text=, for JSON use json_response(data=...).
  3. Register a custom Payload type via payload.PAYLOAD_REGISTRY.register if you need first-class support.

Example fix

# before
resp = Response()
resp.body = {'key': 'value'}  # dict has no payload by default -> ValueError

# after
from aiohttp import json_response
resp = json_response({'key': 'value'})
Defensive patterns

Strategy: type-guard

Validate before calling

def coerce_body(value):
    if isinstance(value, (bytes, bytearray)):
        return value
    if isinstance(value, str):
        return value.encode()
    raise TypeError(f'Use json_response/text= for {type(value)}; body= needs bytes')

Type guard

def is_bytes_body(value) -> bool:
    return isinstance(value, (bytes, bytearray))

Prevention

When it happens

Trigger: Assigning resp.body = some_custom_object that has no registered Payload; passing an int, list, or arbitrary class instance; resp.body = a generator (not a registered payload stream type).

Common situations: Setting body to an object that isn't bytes and has no registered aiohttp payload serializer; forgetting that Response(text=...) handles str while body= does not.

Related errors


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