aio-libs/aiohttp · error · RuntimeError

Called begin_http_chunk_receiving when some data was already

Error message

Called begin_http_chunk_receiving when some data was already fed

What it means

Internal guard in StreamReader.begin_http_chunk_receiving: HTTP chunked-transfer bookkeeping (_http_chunk_splits) is initialised only when no body bytes have been fed yet. If feed_data already delivered bytes and the parser then calls begin_http_chunk_receiving, aiohttp refuses rather than corrupt chunk offsets. This is almost always an aiohttp/parser-internal invariant violation, not user code.

Source

Thrown at aiohttp/streams.py:308

        data_len = len(data)
        self._size += data_len
        self._buffer.append(data)
        self.total_bytes += data_len

        waiter = self._waiter
        if waiter is not None:
            self._waiter = None
            set_result(waiter, None)

        if self._size > self._high_water:
            self._protocol.pause_reading()
        return False

    def begin_http_chunk_receiving(self) -> None:
        if self._http_chunk_splits is None:
            if self.total_bytes:
                raise RuntimeError(
                    "Called begin_http_chunk_receiving when some data was already fed"
                )
            self._http_chunk_splits = collections.deque()

    def end_http_chunk_receiving(self) -> None:
        if self._http_chunk_splits is None:
            raise RuntimeError(
                "Called end_chunk_receiving without calling "
                "begin_chunk_receiving first"
            )

        # self._http_chunk_splits contains logical byte offsets from start of
        # the body transfer. Each offset is the offset of the end of a chunk.
        # "Logical" means bytes, accessible for a user.
        # If no chunks containing logical data were received, current position
        # is difinitely zero.
        pos = self._http_chunk_splits[-1] if self._http_chunk_splits else 0

View on GitHub (pinned to c0ef574e29)

Solutions

  1. Capture the raw response (curl -i) to identify malformed Transfer-Encoding / Content-Length mixing on the server.
  2. If using a custom protocol, do not call feed_data before begin_http_chunk_receiving — let the parser own framing.
  3. Update aiohttp (and the vendored llhttp) in case it is a fixed parser bug.
  4. File an aiohttp issue with the response bytes and headers if it reproduces against a real server.
Defensive patterns

Strategy: try-catch

Try / catch

# Internal invariant — catch at the request boundary and surface as 502.
try:
    resp = await session.get(url)
    body = await resp.read()
except RuntimeError as e:
    if 'begin_http_chunk_receiving' in str(e):
        raise BadGateway(f'upstream framing error: {e}') from e
    raise

Prevention

When it happens

Trigger: Triggered by the HTTP parser (http_parser.py:1015) calling payload.begin_http_chunk_receiving() after bytes were already fed without chunk framing — e.g. a response that changed framing mid-stream, a malformed server mixing Content-Length and chunked, or a buggy/proxy-injected payload. Reproducible mainly through malformed upstream traffic or a custom protocol feeding the StreamReader directly.

Common situations: Talking to a misbehaving proxy (e.g. nginx sub_filter rewriting bodies); receiving a response that started as Content-Length then switched to chunked; using a custom protocol object that mixes feed_data with chunk APIs; mismatch between parser version and streams.py after an in-tree edit.

Related errors


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