aio-libs/aiohttp · error · ConnectionError

Response is sent already, cannot send another response with

Error message

Response is sent already, cannot send another response with the error message

What it means

Raised as ConnectionError by RequestHandler.handle_error (aiohttp/web_protocol.py:864) when an error occurs in the handler but request.writer.output_size > 0 - i.e. bytes have already been written to the wire. aiohttp cannot send a second response with the error status on the same connection (the status line and headers were already flushed), so it raises ConnectionError to signal the connection is unrecoverable and must be torn down rather than emit a malformed response.

Source

Thrown at aiohttp/web_protocol.py:864

    def handle_error(
        self,
        request: BaseRequest,
        status: int = 500,
        exc: BaseException | None = None,
        message: str | None = None,
    ) -> StreamResponse:
        """Handle errors.

        Returns HTTP response with specific status code. Logs additional
        information. It always closes current connection.
        """
        self.log_exception(
            "Error handling request from %s", request.remote, exc_info=exc
        )

        # some data already got sent, connection is broken
        if request.writer.output_size > 0:
            raise ConnectionError(
                "Response is sent already, cannot send another response "
                "with the error message"
            )

        ct = "text/plain"
        if status == HTTPStatus.INTERNAL_SERVER_ERROR:
            title = f"{HTTPStatus.INTERNAL_SERVER_ERROR.value} {HTTPStatus.INTERNAL_SERVER_ERROR.phrase}"
            msg = HTTPStatus.INTERNAL_SERVER_ERROR.description
            tb = None
            if self._loop.get_debug():
                with suppress(Exception):
                    tb = traceback.format_exc()

            if "text/html" in request.headers.get("Accept", ""):
                if tb:
                    tb = html_escape(tb)
                    msg = f"<h2>Traceback:</h2>\n<pre>{tb}</pre>"
                message = (

View on GitHub (pinned to c0ef574e29)

Solutions

  1. Buffer the full response and only flush once all work has succeeded (prepare data first, then write).
  2. If you must stream, design a protocol that tolerates truncation (e.g. SSE 'event: error' frames) instead of relying on aiohttp to emit a clean error.
  3. Catch anticipated errors in the handler itself before any bytes are written so handle_error never runs into the output_size > 0 branch.
  4. Log ConnectionError from handle_error at debug, since the client already has partial data.

Example fix

// before
async def handler(request):
    resp = web.StreamResponse()
    await resp.prepare(request)
    await resp.write(b"{\"data\":")
    rows = await db.fetch()  # raises here -> ConnectionError
    await resp.write(json.dumps(rows).encode())
    return resp

# after
async def handler(request):
    rows = await db.fetch()          # fail before writing
    return web.json_response({"data": rows})
Defensive patterns

Strategy: try-catch

Type guard

def has_sent_bytes(resp: web.StreamResponse) -> bool:
    return getattr(resp, "prepared", False)

Try / catch

try:
    return await handler(request)
except ConnectionError:
    logger.debug("client disconnect after partial write")
    raise

Prevention

When it happens

Trigger: A handler calls resp.write(chunk) (or yield s in a StreamResponse body, or prepares a response) and then raises an exception afterwards - e.g. a streaming endpoint that emits part of a JSON document and then hits a DB error. handle_error is invoked by the error path, sees output_size > 0, and raises this ConnectionError.

Common situations: Streaming JSON / SSE / chunked responses where a downstream failure happens mid-stream; handlers that begin writing before they have fully validated work; partial HTML renders that fail in the middle of a template.

Related errors


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