microsoft/autogen · error · RuntimeError

Runtime is not started

Error message

Runtime is not started

What it means

stop() raises RuntimeError when self._run_context is None, meaning the runtime's processing loop is not currently running. Stopping requires an active run context; stopping an idle/never-started (or already-stopped) runtime is an error, not a no-op.

Source

Thrown at python/packages/autogen-core/src/autogen_core/_single_threaded_agent_runtime.py:836

        """
        if self._run_context is not None:
            raise RuntimeError("Runtime is already started")
        self._run_context = RunContext(self)

    async def close(self) -> None:
        """Calls :meth:`stop` if applicable and the :meth:`Agent.close` method on all instantiated agents"""
        # stop the runtime if it hasn't been stopped yet
        if self._run_context is not None:
            await self.stop()
        # close all the agents that have been instantiated
        for agent_id in self._instantiated_agents:
            agent = await self._get_agent(agent_id)
            await agent.close()

    async def stop(self) -> None:
        """Immediately stop the runtime message processing loop. The currently processing message will be completed, but all others following it will be discarded."""
        if self._run_context is None:
            raise RuntimeError("Runtime is not started")

        try:
            await self._run_context.stop()
        finally:
            self._run_context = None
            self._message_queue = Queue()

    async def stop_when_idle(self) -> None:
        """Stop the runtime message processing loop when there is
        no outstanding message being processed or queued. This is the most common way to stop the runtime."""
        if self._run_context is None:
            raise RuntimeError("Runtime is not started")

        try:
            await self._run_context.stop_when_idle()
        finally:
            self._run_context = None
            self._message_queue = Queue()

View on GitHub (pinned to 027ecf0a37)

Solutions

  1. Call stop() exactly once per start(); track lifecycle in your orchestrator with a flag or use try/except RuntimeError in cleanup as a deliberate idempotency guard
  2. Prefer await runtime.close() for teardown: it stops only if not already stopped and also closes agents
  3. Ensure close()/stop() are not both invoked in the same teardown sequence for the stop portion

Example fix

# before
await runtime.stop_when_idle()
await runtime.stop()  # RuntimeError

# after
await runtime.stop_when_idle()
await runtime.close()  # safe: no-ops the stop portion, closes agents
Defensive patterns

Strategy: try-catch

Validate before calling

def can_stop(runtime) -> bool:
    return runtime._run_context is not None

Try / catch

try:
    await runtime.stop()
except RuntimeError as e:
    if "not started" in str(e):
        pass  # already stopped; idempotent teardown
    else:
        raise

Prevention

When it happens

Trigger: Calling await runtime.stop() before any start(), after stop()/stop_when_idle() already completed, or after close() (which itself calls stop when applicable). Double-stop in cleanup paths is the most common form.

Common situations: Cleanup/teardown code running on both the success and exception paths so stop() executes twice; orchestrators that stop at end-of-task and again at shutdown; tests tearing down a runtime that a previous line already stopped.

Related errors


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