microsoft/semantic-kernel · error · RuntimeError

Runtime is already started

Error message

Runtime is already started

What it means

InProcessRuntime.start() sets _run_context. If _run_context is already set (runtime was started and not stopped), calling start() again raises RuntimeError. The runtime must be stopped before it can be restarted.

Solutions

  1. Call await runtime.stop() (or stop_when_idle) before calling start() again.
  2. Use runtime.close() for full teardown, which internally calls stop().
  3. Create a fresh InProcessRuntime instance for each independent run rather than reusing one.

Example fix

# before
runtime.start()
# ... use runtime ...
runtime.start()  # raises 'Runtime is already started'

# after
runtime.start()
# ... use runtime ...
await runtime.stop_when_idle()
runtime.start()  # ok
Defensive patterns

Strategy: validation

Validate before calling

# Check runtime state before starting
if runtime._run_context is not None:
    raise RuntimeError('Runtime already started — stop it first')
runtime.start()

Type guard

null

Try / catch

try:
    runtime.start()
except RuntimeError:
    await runtime.stop()
    runtime.start()

Prevention

When it happens

Trigger: Calling runtime.start() twice without an intervening runtime.stop() (or stop_when_idle/stop_when). Common in test teardown/re-setup cycles or when reusing a runtime instance across runs.

Common situations: A test fixture starts the runtime in setUp but forgets to stop it in tearDown, then a second test calls start() on the same instance. Or application code that restarts the runtime on reconnect/retry without stopping first.

Related errors


AI-assisted analysis of microsoft/semantic-kernel@c028a0c7dc (2026-08-13). Data as JSON: /api/errors/8fde69f52ca7734e. Report an issue: GitHub.

Appendix: source

Thrown at python/semantic_kernel/agents/runtime/in_process/in_process_runtime.py:655

                                    sender=sender,
                                    receiver=recipient,
                                    kind=MessageKind.RESPOND,
                                )
                            )
                            future.set_exception(MessageDroppedException())
                            return
                        message_envelope.message = temp_message
                task = asyncio.create_task(self._process_response(message_envelope))
                self._background_tasks.add(task)
                task.add_done_callback(self._background_tasks.discard)

        # Yield control to the message loop to allow other tasks to run
        await asyncio.sleep(0)

    def start(self) -> None:
        """Start the runtime message processing loop. This runs in a background task."""
        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:

View on GitHub (pinned to c028a0c7dc)