microsoft/aspire · error · ConnectionError

Connection closed

Error message

Connection closed

What it means

Raised by _recv_exactly in the generated Python client when the underlying pipe/socket returns an empty chunk before the requested number of bytes has been read. An empty recv means the peer closed the connection, so a complete message can no longer be assembled.

Solutions

  1. Catch ConnectionError and re-establish the connection before retrying the operation.
  2. Verify the host process was alive for the duration of the call; inspect host logs for crashes.
  3. Reduce the chance of mid-response disconnects by keeping requests within host-side deadlines.
  4. Treat any in-flight partial message as discarded after reconnect (the protocol has no resume).

Example fix

// before
response = client.invoke_capability("get_items", args)

// after
try:
    response = client.invoke_capability("get_items", args)
except ConnectionError:
    client.reconnect()
    response = client.invoke_capability("get_items", args)
Defensive patterns

Strategy: try-catch

Try / catch

try:
    response = client.invoke_capability(cap, args)
except ConnectionError:
    client.reconnect()
    response = client.invoke_capability(cap, args)

Prevention

When it happens

Trigger: Calling any client method that reads a length-prefixed response after the host has closed or exited; host restarting between request and response; abrupt process kill mid-response.

Common situations: AppHost stopped while the client holds an idle connection; host crash mid-request; timeout/kill on the server side for a long-running capability.

Understand the failure class

Related errors


AI-assisted analysis of microsoft/aspire@25830f84bd (2026-09-16). Data as JSON: /api/errors/0a0d98d3ad763b88. Report an issue: GitHub.

Appendix: source

Thrown at src/Aspire.Hosting.CodeGeneration.Python/PythonModuleBuilder.cs:906

                signal.signal(signal.SIGINT, self._handle_sigint)

                _logger.info("Connected to AppHost")

                # Start receiving messages in a background thread
                self._receive_thread = threading.Thread(target=self._receive_loop, daemon=True)
                self._receive_thread.start()

                # Start heartbeat thread to monitor connection health
                self._heartbeat_thread = threading.Thread(target=self._heartbeat_loop, daemon=True)
                self._heartbeat_thread.start()

            def _recv_exactly(self, n: int) -> bytes:
                '''Read exactly n bytes from the socket'''
                data = b""
                while len(data) < n:
                    chunk = typing.cast(_PipeSocket, self._socket).recv(n - len(data))
                    if not chunk:
                        raise ConnectionError("Connection closed")
                    data += chunk
                return data

            def _read_line(self) -> bytes:
                '''Read a line ending with \\r\\n from the socket'''
                line = b""
                while True:
                    byte = typing.cast(_PipeSocket, self._socket).recv(1)
                    if not byte:
                        raise ConnectionError("Connection closed")
                    line += byte
                    if line.endswith(b"\r\n"):
                        return line[:-2]  # Remove \r\n

            def _read_headers(self) -> dict[str, str]:
                '''Read HTTP-style headers until empty line.

                Enforces limits on header count and total size to prevent

View on GitHub (pinned to 25830f84bd)