microsoft/autogen · error · ValueError
Runtime is already running.
Error message
Runtime is already running.
What it means
GrpcWorkerAgentRuntime.start() is not idempotent: it checks the internal _running flag and raises ValueError('Runtime is already running.') if start() is called again without an intervening stop(). This protects the single read-loop task and host connection from being duplicated.
Source
Thrown at python/packages/autogen-ext/src/autogen_ext/runtimes/grpc/_worker_runtime.py:264
self._pending_requests: Dict[str, Future[Any]] = {}
self._pending_requests_lock = asyncio.Lock()
self._next_request_id = 0
self._host_connection: HostConnection | None = None
self._background_tasks: Set[Task[Any]] = set()
self._subscription_manager = SubscriptionManager()
self._serialization_registry = SerializationRegistry()
self._extra_grpc_config = extra_grpc_config or []
self._agent_instance_types: Dict[str, Type[Agent]] = {}
if payload_serialization_format not in {JSON_DATA_CONTENT_TYPE, PROTOBUF_DATA_CONTENT_TYPE}:
raise ValueError(f"Unsupported payload serialization format: {payload_serialization_format}")
self._payload_serialization_format = payload_serialization_format
async def start(self) -> None:
"""Start the runtime in a background task."""
if self._running:
raise ValueError("Runtime is already running.")
logger.info(f"Connecting to host: {self._host_address}")
self._host_connection = await HostConnection.from_host_address(
self._host_address, extra_grpc_config=self._extra_grpc_config
)
logger.info("Connection established")
if self._read_task is None:
self._read_task = asyncio.create_task(self._run_read_loop())
self._running = True
def _raise_on_exception(self, task: Task[Any]) -> None:
exception = task.exception()
if exception is not None:
raise exception
async def _run_read_loop(self) -> None:
logger.info("Starting read loop")
assert self._host_connection is not None
# TODO: catch exceptions and reconnectView on GitHub (pinned to 027ecf0a37)
Solutions
- Track started state yourself and only call start() once per lifecycle
- Catch/avoid: check runtime state or guard with try/except ValueError if double-start is possible
- Call `await runtime.stop()` (and optionally `await runtime.close()`) before restarting in restart loops
Example fix
# before
await runtime.start() # ValueError on second call
# after
started = False
if not started:
await runtime.start()
started = True Defensive patterns
Strategy: validation
Validate before calling
# start only if not running (track locally; _running is private)
started = False
async def ensure_started(runtime):
global started
if not started:
await runtime.start()
started = True Try / catch
try:
await runtime.start()
except ValueError as e:
if "already running" in str(e):
pass # idempotent start
else:
raise Prevention
- Wrap runtime lifecycle in an async context manager so start() is called exactly once
- Avoid retry loops around start() without state tracking
- In notebooks, reuse one runtime across cells instead of re-running start()
When it happens
Trigger: Calling `await runtime.start()` twice, e.g. in a retry wrapper, a Jupyter notebook re-execution, or after a framework (like an agent runtime host) already started the runtime for you.
Common situations: Notebook workflows where a cell is re-run; orchestration code that starts the runtime per-task inside a long-lived process; wrapping start() in a generic retry loop.
Related errors
- Connection is not open.
- Runtime is not running.
- Host connection is not set.
- Runtime must be running when sending message.
- Runtime must be running when publishing message.
AI-assisted analysis of microsoft/autogen@027ecf0a37 (2026-08-15).
Data as JSON: /api/errors/ee832d57b87f5fa1.
Report an issue: GitHub.