aio-libs/aiohttp · error · BadHttpMessage
Data after `Connection: close`
Error message
Data after `Connection: close`
What it means
Raised by HttpParser.feed_data (aiohttp/http_parser.py:370) after a message with should_close=True was fully parsed and additional bytes arrive on the same connection before EOF. should_close is set by Connection: close, HTTP/1.0 defaults, or error-status heuristics. Extra bytes after a close-signaled message violate connection semantics (pipelining after close) and are rejected to avoid mis-attributing them to the closed message.
Source
Thrown at aiohttp/http_parser.py:370
if self._payload_parser is None and not self._upgraded:
if (
self._max_msg_queue_size
and self._msg_in_flight >= self._max_msg_queue_size
):
# Queue full: buffer the rest and stop. Safe pause point;
# any preceding body is consumed before the next request
# line. Resumes via feed_data(b"") when the queue drains.
self._tail = data[start_pos:]
break
pos = data.find(SEP, start_pos)
# consume \r\n
if pos == start_pos and not self._lines:
start_pos = pos + len(SEP)
continue
if pos >= start_pos:
if should_close:
raise BadHttpMessage("Data after `Connection: close`")
# line found
line = data[start_pos:pos]
if SEP == b"\n": # For lax response parsing
line = line.rstrip(b"\r")
if len(line) > max_line_length:
raise LineTooLong(line[:100] + b"...", max_line_length)
self._lines.append(line)
# After processing the status/request line, everything is a header.
max_line_length = self.max_field_size
if len(self._lines) > self.max_headers:
raise BadHttpMessage("Too many headers received")
start_pos = pos + len(SEP)
# \r\n\r\n foundView on GitHub (pinned to c0ef574e29)
Solutions
- Do not pipeline requests on a connection the peer signaled to close; open a new connection instead.
- On the server side, let aiohttp close the socket and do not attempt to read more from it.
- If you see this as a client, the server closed earlier than expected - retry the request on a fresh ClientSession/connection.
- Review Connection: close semantics in any proxy between client and server.
Example fix
# before - reusing a session the server is closing
async with session.get(url, headers={'Connection': 'close'}) as r1:
...
async with session.get(url) as r2: # same pool, server closing -> error
...
# after - open a fresh connection
async with aiohttp.ClientSession() as s2:
async with s2.get(url) as r2:
... Defensive patterns
Strategy: retry
Try / catch
from aiohttp import http_exceptions
for attempt in range(3):
try:
async with session.post(url, data=payload) as r:
return await r.read()
except http_exceptions.BadHttpMessage as e:
if 'Connection: close' in str(e) and attempt < 2:
await asyncio.sleep(0.1 * (attempt + 1))
continue
raise Prevention
- Respect Connection: close - never reuse or pipeline on the socket
- Retry idempotent requests on a fresh connection if a close arrives early
- Disable client-side pipelining when the peer signals close
When it happens
Trigger: A peer sends a request/response with Connection: close (or HTTP/1.0) and then immediately pipelines another request on the same socket before it closes. Or a client reuses a socket the server already decided to close. The check fires on the NEXT line found after should_close was set.
Common situations: Keep-alive misconfiguration, clients that reuse a closing socket (common when a proxy/load-balancer decides to close mid-pipeline), or race conditions where pipelined requests overtake the close decision.
Related errors
- Got more than {limit} bytes when reading: {line!r}.
- Duplicate '{name}' header found.
- Too many headers received
- Bad line ending, expected CRLF
- Transfer-Encoding can't be present with Content-Length
AI-assisted analysis of aio-libs/aiohttp@c0ef574e29 (2026-08-04).
Data as JSON: /data/errors/691846c3489052e5.json.
Report an issue: GitHub.