aio-libs/aiohttp · error · RuntimeError

Content length is set automatically

Error message

Content length is set automatically

What it means

Response.content_length is a read-only computed property: its value is derived from the body length, compressed body, or Payload size (lines 657-674). The setter unconditionally raises RuntimeError because manually setting it would desynchronize the header from the actual bytes sent.

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

Solutions

  1. Let aiohttp compute content_length automatically from the body.
  2. If you need a specific length, set the body to exactly that many bytes (or use a Payload with .size).
  3. For low-level control use StreamResponse (whose content_length setter is allowed), not Response.

Example fix

# before
resp = Response(text='hi')
resp.content_length = 2  # raises RuntimeError on Response

# after
resp = Response(text='hi')  # content_length computed automatically as 2
Defensive patterns

Strategy: validation

Validate before calling

# Response.content_length is read-only; do not assign.
# If you need manual length control, use StreamResponse:
# resp = StreamResponse(headers={'Content-Length': str(n)})

Prevention

When it happens

Trigger: Calling `resp.content_length = 123` on a Response instance. (Note: StreamResponse.content_length setter at line 208-209 does allow assignment; only the Response subclass forbids it.)

Common situations: Trying to pre-set a content length before streaming; copying StreamResponse code onto a Response;HEAD/304 handling where devs attempt to force a length.

Related errors


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