aio-libs/aiohttp · error · ValueError

chunked can not be set if Content-Length header is set

Error message

chunked can not be set if Content-Length header is set

What it means

Raised by ClientRequest._update_transfer_encoding() when chunked transfer encoding is being enabled but a Content-Length header is already present. A message cannot have both a Content-Length (fixed size) and chunked Transfer-Encoding (unknown size); they are mutually exclusive per HTTP semantics, so aiohttp raises ValueError.

Solutions

  1. For fixed-size bodies, let aiohttp compute Content-Length from the data and don't set it manually.
  2. For streaming/unknown-size bodies, remove any manual Content-Length header.
  3. If you set Content-Length, ensure the body is bytes/str with matching length and not a stream.
  4. Audit header construction to avoid stale Content-Length from a template.

Example fix

# before
headers['Content-Length'] = '100'
await session.post(url, data=async_gen(), headers=headers)  # ValueError

# after — streaming body, no manual Content-Length
await session.post(url, data=async_gen(), headers={k:v for k,v in headers.items() if k.lower()!='content-length'})
Defensive patterns

Strategy: validation

Validate before calling

def strip_conflicting_length(headers, body):
    # streaming/unknown-size body cannot have a preset Content-Length
    if not isinstance(body, (bytes, bytearray, str)):
        headers.pop('Content-Length', None)
    return headers

Type guard

def body_and_headers_consistent(headers, body) -> bool:
    streaming = not isinstance(body, (bytes, bytearray, str, type(None)))
    return not (streaming and any(k.lower() == 'content-length' for k in headers))

Try / catch

try:
    await session.post(url, data=stream_gen, headers=headers)
except ValueError as e:
    if 'Content-Length header is set' in str(e):
        headers = {k:v for k,v in headers.items() if k.lower() != 'content-length'}
        await session.post(url, data=stream_gen, headers=headers)
    else:
        raise

Prevention

When it happens

Trigger: Setting headers['Content-Length']='123' AND triggering chunked mode (via a streaming body, compress=True, or chunked=True). _update_transfer_encoding sees self.chunked True and Content-Length in headers, and raises ValueError.

Common situations: Pre-setting Content-Length for a body whose actual size is unknown (stream); copy-pasting headers that include Content-Length while passing a generator/async generator body; mixing fixed-length assumptions with chunked producers.

Related errors


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

Appendix: source

Thrown at aiohttp/client_reqrep.py:1235

                )
            self.compress = compress if isinstance(compress, str) else "deflate"
            self.headers[hdrs.CONTENT_ENCODING] = self.compress
            self.chunked = True  # enable chunked, no need to deal with length

    def _update_transfer_encoding(self) -> None:
        """Analyze transfer-encoding header."""
        te = self.headers.get(hdrs.TRANSFER_ENCODING, "").lower()

        if "chunked" in te:
            if self.chunked:
                raise ValueError(
                    "chunked can not be set "
                    'if "Transfer-Encoding: chunked" header is set'
                )

        elif self.chunked:
            if hdrs.CONTENT_LENGTH in self.headers:
                raise ValueError(
                    "chunked can not be set if Content-Length header is set"
                )

            self.headers[hdrs.TRANSFER_ENCODING] = "chunked"

    def _update_body_from_data(self, body: Any) -> None:
        """Update request body from data."""
        if body is None:
            self._body = self._EMPTY_BODY
            # Set Content-Length to 0 when body is None for methods that expect a body
            if (
                self.method not in self.GET_METHODS
                and not self.chunked
                and hdrs.CONTENT_LENGTH not in self.headers
            ):
                self.headers[hdrs.CONTENT_LENGTH] = "0"
            return

View on GitHub (pinned to d041d4d0fd)