aio-libs/aiohttp · error · RuntimeError

Content length is set automatically

Error message

Content length is set automatically

What it means

Raised by the Response.content_length setter, which is intentionally a no-op that always raises. Content-Length is computed automatically from the body (len of bytes, compressed body, or 0 for None) during preparation; allowing manual override would desync the header from the actual bytes written.

Solutions

  1. Provide the full body up front via `body=`/`text=` and let aiohttp compute the length.
  2. For fixed-size streaming, buffer the chunks and pass the joined bytes as body.
  3. To suppress Content-Length (e.g. chunked), use a StreamResponse with chunked encoding rather than mutating content_length.
  4. Remove the assignment entirely; the property is read-only by design.

Example fix

// before
resp = Response(body=data)
resp.content_length = len(data)  # RuntimeError

// after
resp = Response(body=data)  # content_length is auto-derived; do not set
Defensive patterns

Strategy: validation

Validate before calling

# content_length is read-only; never assign to it.
# Provide the full body and let aiohttp compute length.
resp = Response(body=data)
length = resp.content_length  # read, do not write

Prevention

When it happens

Trigger: Calling `resp.content_length = 1234` anywhere in handler code. Often a copy-paste from setting other headers, or attempting to pre-declare length on a streaming response.

Common situations: Developers used to setting Content-Length manually in other frameworks; trying to fix a streaming response that has no length by setting the property; refactoring that mistakenly assigns to the property.

Related errors


AI-assisted analysis of aio-libs/aiohttp@d041d4d0fd (2026-08-11). Data as JSON: /api/errors/171450abd88167cb. Report an issue: GitHub.

Appendix: source

Thrown at aiohttp/web_response.py:678

            return None

        if hdrs.CONTENT_LENGTH in self._headers:
            return int(self._headers[hdrs.CONTENT_LENGTH])

        if self._compressed_body is not None:
            # Return length of the compressed body
            return len(self._compressed_body)
        elif isinstance(self._body, Payload):
            # A payload without content length, or a compressed payload
            return None
        elif self._body is not None:
            return len(self._body)
        else:
            return 0

    @content_length.setter
    def content_length(self, value: int | None) -> None:
        raise RuntimeError("Content length is set automatically")

    async def write_eof(self, data: bytes = b"") -> None:
        if self._eof_sent:
            return
        if self._compressed_body is None:
            body = self._body
        else:
            body = self._compressed_body
        assert not data, f"data arg is not supported, got {data!r}"
        assert self._req is not None
        assert self._payload_writer is not None
        if body is None or self._must_be_empty_body:
            await super().write_eof()
        elif isinstance(self._body, Payload):
            try:
                await self._body.write(self._payload_writer)
            finally:
                await self._body.close()

View on GitHub (pinned to d041d4d0fd)