aio-libs/aiohttp · error · TransferEncodingError
Not enough data to satisfy transfer length header.
Error message
Not enough data to satisfy transfer length header.
What it means
Raised as TransferEncodingError (a BadHttpMessage subclass) when a chunked-encoded body stream reaches EOF before the terminating zero-length chunk (and trailers). HttpPayloadParser.feed_eof, in the PARSE_CHUNKED branch, raises unconditionally: a properly terminated chunked body finishes via the size-0 chunk path, never via EOF. So hitting EOF in this state means the chunked stream was truncated.
Solutions
- Ensure the sender of a chunked body always emits the final '0\r\n\r\n' before closing.
- On the receiving side, treat this as a fatal connection error; reconnect and retry idempotent requests.
- Inspect intermediaries (proxies, load balancers) that may strip or truncate the terminating chunk.
- If streaming cannot be guaranteed to complete, consider a different framing or reconnect logic.
- Check the sender's logs for unhandled exceptions during chunk production.
Example fix
# before (server-side handler crashes before write_eof)
resp = web.StreamResponse(headers={'Transfer-Encoding': 'chunked'})
await resp.prepare(request)
await resp.write(b'some data')
# ... exception raised here, connection closes without final chunk
# after (ensure write_eof runs in a finally)
resp = web.StreamResponse()
await resp.prepare(request)
try:
await resp.write(b'some data')
finally:
await resp.write_eof() Defensive patterns
Strategy: retry
Try / catch
from aiohttp.http_exceptions import TransferEncodingError
import aiohttp
async def fetch_chunked(url, *, attempts=3):
last = None
for i in range(attempts):
try:
async with aiohttp.ClientSession() as s:
async with s.get(url) as r:
return await r.read()
except TransferEncodingError as e:
last = e
await asyncio.sleep(2 ** i)
raise last
Prevention
- Ensure chunked senders emit the final '0\r\n\r\n' terminator.
- Retry idempotent requests on truncated chunked streams.
- Audit proxies that may strip the terminating chunk.
- Always run write_eof() in a finally block when streaming.
When it happens
Trigger: A request/response declares 'Transfer-Encoding: chunked' but the connection closes before the final '0\r\n\r\n' terminator arrives. Fires when feed_eof is called while _type == ParseState.PARSE_CHUNKED.
Common situations: The server/client closes the connection mid-chunk without sending the terminating zero-size chunk; a proxy or CDN severing a streaming response; network resets; a misbehaving streaming endpoint that crashes before finishing the chunked frame; clients that give up reading and abort.
Related errors
- Not enough data to satisfy content length header
- Request has duplicate `chunked` Transfer-Encoding
- Request has invalid `Transfer-Encoding`
- chunked can not be set if Content-Length header is set
- chunked can not be set if "Transfer-Encoding: chunked"…
AI-assisted analysis of aio-libs/aiohttp@d041d4d0fd (2026-08-11).
Data as JSON: /api/errors/24859c8172b3e955.
Report an issue: GitHub.
Appendix: source
Thrown at aiohttp/http_parser.py:934
self.done = True
self._eof_pending = False
elif self._type == ParseState.PARSE_LENGTH:
if self._length:
received = self._length_expected - self._length
raise ContentLengthError(
f"Not enough data to satisfy content length header "
f"(received {received} of {self._length_expected} bytes)."
)
# Body has already been received, but parser paused.
while self._more_data_available:
if self._paused:
self._paused = False
return # Will resume via feed_data(b"") later
self._more_data_available = self.payload.feed_data(b"")
self.payload.feed_eof()
self.done = True
elif self._type == ParseState.PARSE_CHUNKED:
raise TransferEncodingError(
"Not enough data to satisfy transfer length header."
)
def feed_data(
self, chunk: bytes, SEP: _SEP = b"\r\n", CHUNK_EXT: bytes = b";"
) -> tuple[PayloadState, bytes]:
"""Receive a chunk of data to process.
Return:
PayloadState - The current state of payload processing.
This function may be called with empty bytes after returning
PAYLOAD_HAS_PENDING_INPUT to continue processing after a pause.
bytes - If payload is complete, this is the unconsumed bytes intended for the
next message/payload, b"" otherwise.
"""
# Read specified amount of bytes
if self._type == ParseState.PARSE_LENGTH:
if self._chunk_tail:View on GitHub (pinned to d041d4d0fd)