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 found

View on GitHub (pinned to c0ef574e29)

Solutions

  1. Do not pipeline requests on a connection the peer signaled to close; open a new connection instead.
  2. On the server side, let aiohttp close the socket and do not attempt to read more from it.
  3. If you see this as a client, the server closed earlier than expected - retry the request on a fresh ClientSession/connection.
  4. 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

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


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