microsoft/autogen · error · RuntimeError

Connection is not open.

Error message

Connection is not open.

What it means

HostConnection.close() (returned by GrpcWorkerAgentRuntimeHost.connect / from_host_address) requires an active connection task; if _connection_task is None the connection was never opened (or already closed), and close() raises RuntimeError instead of silently succeeding.

Source

Thrown at python/packages/autogen-ext/src/autogen_ext/runtimes/grpc/_worker_runtime.py:159

            (k, v) for k, v in {**dict(HostConnection.DEFAULT_GRPC_CONFIG), **dict(extra_grpc_config)}.items()
        ]

        channel = grpc.aio.insecure_channel(
            host_address,
            options=merged_options,
        )
        stub: AgentRpcAsyncStub = agent_worker_pb2_grpc.AgentRpcStub(channel)  # type: ignore
        instance = cls(channel, stub)

        instance._connection_task = await instance._connect(
            stub, instance._send_queue, instance._recv_queue, instance._client_id
        )

        return instance

    async def close(self) -> None:
        if self._connection_task is None:
            raise RuntimeError("Connection is not open.")
        await self._channel.close()
        await self._connection_task

    @staticmethod
    async def _connect(
        stub: Any,  # AgentRpcAsyncStub
        send_queue: asyncio.Queue[agent_worker_pb2.Message],
        receive_queue: asyncio.Queue[agent_worker_pb2.Message],
        client_id: str,
    ) -> Task[None]:
        from grpc.aio import StreamStreamCall

        # TODO: where do exceptions from reading the iterable go? How do we recover from those?
        stream: StreamStreamCall[agent_worker_pb2.Message, agent_worker_pb2.Message] = stub.OpenChannel(  # type: ignore
            QueueAsyncIterable(send_queue), metadata=[("client-id", client_id)]
        )

        await stream.wait_for_connection()

View on GitHub (pinned to 027ecf0a37)

Solutions

  1. Only call close() on a fully constructed, successfully opened connection — track whether connect succeeded
  2. In finally blocks, guard with a None check or try/except RuntimeError
  3. Use the runtime's own stop()/close() lifecycle instead of managing the raw connection

Example fix

# before
conn = None
try:
    conn = await SomeCls.connect(...)
finally:
    await conn.close()  # RuntimeError if connect failed

# after
finally:
    if conn is not None:
        await conn.close()
Defensive patterns

Strategy: try-catch

Validate before calling

def can_close(conn) -> bool:
    return getattr(conn, "_connection_task", None) is not None

Try / catch

try:
    await conn.close()
except RuntimeError as e:
    if "Connection is not open" in str(e):
        pass  # nothing to close
    else:
        raise

Prevention

When it happens

Trigger: Calling `await connection.close()` before ever calling connect()/from_host_address successfully, or calling close() twice (though double-close typically fails differently); in this adapter path, closing a connection whose _connection_task attribute is still None.

Common situations: Error-handling paths that close a connection in a finally block even when setup failed halfway; race where an exception during connect leaves a partially constructed instance that still gets closed.

Related errors


AI-assisted analysis of microsoft/autogen@027ecf0a37 (2026-08-15). Data as JSON: /api/errors/02c8345045870ad7. Report an issue: GitHub.