{"record":{"id":"46a3b324bf7096b0","repo":"microsoft/semantic-kernel","slug":"unable-to-proceed-while-another-agent-is-active-46a3b3","errorCode":null,"errorMessage":"Unable to proceed while another agent is active.","messagePattern":"Unable to proceed while another agent is active\\.","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"python/semantic_kernel/agents/group_chat/agent_chat.py","lineNumber":45,"sourceCode":"\n    broadcast_queue: BroadcastQueue = Field(default_factory=BroadcastQueue)\n    agent_channels: dict[str, AgentChannel] = Field(default_factory=dict)\n    channel_map: dict[Agent, str] = Field(default_factory=dict)\n    history: ChatHistory = Field(default_factory=ChatHistory)\n\n    _lock: threading.Lock = PrivateAttr(default_factory=threading.Lock)\n    _is_active: bool = False\n\n    @property\n    def is_active(self) -> bool:\n        \"\"\"Indicates whether the agent is currently active.\"\"\"\n        return self._is_active\n\n    def set_activity_or_throw(self):\n        \"\"\"Set the activity signal or throw an exception if another agent is active.\"\"\"\n        with self._lock:\n            if self._is_active:\n                raise Exception(\"Unable to proceed while another agent is active.\")\n            self._is_active = True\n\n    def clear_activity_signal(self):\n        \"\"\"Clear the activity signal.\"\"\"\n        with self._lock:\n            self._is_active = False\n\n    def invoke(self, agent: Agent | None = None, is_joining: bool = True) -> AsyncIterable[ChatMessageContent]:\n        \"\"\"Invoke the agent asynchronously.\"\"\"\n        raise NotImplementedError(\"Subclasses should implement this method\")\n\n    async def get_messages_in_descending_order(self) -> AsyncIterable[ChatMessageContent]:\n        \"\"\"Get messages in descending order asynchronously.\"\"\"\n        for index in range(len(self.history.messages) - 1, -1, -1):\n            yield self.history.messages[index]\n            await asyncio.sleep(0)  # Yield control to the event loop\n\n    async def get_chat_messages(self, agent: \"Agent | None\" = None) -> AsyncIterable[ChatMessageContent]:","sourceCodeStart":27,"sourceCodeEnd":63,"githubUrl":"https://github.com/microsoft/semantic-kernel/blob/c028a0c7dc4f0814cdcbaba9d998f187a41197bf/python/semantic_kernel/agents/group_chat/agent_chat.py#L27-L63","documentation":"Raised by AgentChat.set_activity_or_throw() with a bare Exception when _is_active is already True. AgentChat uses a non-reentrant activity flag protected by a threading.Lock to serialize chat operations (add_chat_messages, invoke_agent, get_chat_messages, reset). If one operation is still in flight (e.g. an async generator not fully consumed), the flag remains True and the next operation throws.","triggerScenarios":"Calling a second AgentChat operation while the first is still active: e.g. starting chat.add_chat_messages then, before it completes, calling chat.invoke_agent; or not fully iterating an async generator from invoke/get_chat_messages before starting another operation; concurrent/awaited tasks sharing the same AgentChat instance.","commonSituations":"Not fully consuming an async generator (breaking out of 'async for' early leaves the activity flag set until GC runs the finally); concurrent asyncio tasks using the same AgentGroupChat; calling add_chat_message inside a loop that also invokes agents without awaiting completion.","solutions":["Fully consume every async generator from invoke/invoke_stream/get_chat_messages before starting the next operation.","Serialize operations with an asyncio.Lock at the call site if concurrent access is needed.","If the flag is stuck due to an abandoned generator, call chat.reset() or manually chat.clear_activity_signal() to recover.","Avoid sharing one AgentChat/AgentGroupChat instance across concurrent tasks."],"exampleFix":"# before — generator abandoned early, _is_active stays True\nasync for msg in chat.invoke_agent(agent):\n    print(msg.content)\n    break  # generator not exhausted\nawait chat.add_chat_message(\"next\")  # raises\n\n# after — exhaust the generator or use anexplicit lock\nasync for msg in chat.invoke_agent(agent):\n    print(msg.content)\n# generator fully consumed, activity flag cleared\nawait chat.add_chat_message(\"next\")","handlingStrategy":"validation","validationCode":"if chat.is_active:\n    raise RuntimeError(\"Chat is active; finish or cancel the current operation first\")\nawait chat.add_chat_message(\"hello\")","typeGuard":null,"tryCatchPattern":"try:\n    await chat.add_chat_message(\"hello\")\nexcept Exception as exc:\n    if \"another agent is active\" in str(exc):\n        chat.clear_activity_signal()  # recover from a stuck flag\n        await chat.add_chat_message(\"hello\")\n    raise","preventionTips":["Fully consume every async generator from invoke/invoke_stream before the next operation.","Do not share one AgentChat/AgentGroupChat across concurrent asyncio tasks; use separate instances or serialize with asyncio.Lock.","If a generator is abandoned early, call clear_activity_signal() to reset the flag."],"tags":["concurrency","thread-safety","group-chat","async"],"backgroundTag":null,"analyzedSha":"c028a0c7dc4f0814cdcbaba9d998f187a41197bf","analyzedAt":"2026-08-13T13:48:05.040Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}