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
- Capture the raw response (curl -i) to identify malformed Transfer-Encoding / Content-Length mixing on the server.
- If using a custom protocol, do not call feed_data before begin_http_chunk_receiving — let the parser own framing.
- Update aiohttp (and the vendored llhttp) in case it is a fixed parser bug.
- 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
- Do not feed bytes to a StreamReader manually while using the chunk API.
- Keep aiohttp and llhttp current to pick up parser fixes.
- Capture full upstream bytes when this reproduces so you can file an issue.
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
- Called end_chunk_receiving without calling begin_chunk_recei
- EmptyStreamReader._on_chunk_received is read-only
- Invalid HTTP header: {hdr!r}
- Not enough data to satisfy transfer length header.
- Too many trailers received
AI-assisted analysis of aio-libs/aiohttp@c0ef574e29 (2026-08-04).
Data as JSON: /data/errors/c33e7c9ee40c8b14.json.
Report an issue: GitHub.