From e99c5bbae8058f7f581737caf226031874e6165f Mon Sep 17 00:00:00 2001 From: giulio-leone Date: Fri, 13 Mar 2026 05:00:13 +0100 Subject: [PATCH 1/2] fix(graph): prevent reset_executor_state from corrupting MultiAgentBase state GraphNode.reset_executor_state() checked hasattr(self.executor, 'state') but did not verify the state type before overwriting it with AgentState. When the executor is a MultiAgentBase (e.g. a nested Graph), its state is a GraphState, and overwriting it with AgentState corrupts the executor. The __post_init__ method already had the correct guard: hasattr(self.executor.state, 'get') but reset_executor_state() was missing it. This affected two call sites: - _execute_node (when reset_on_revisit is enabled with nested graphs) - deserialize_state (unconditionally resets all nodes on completed runs) Fixes #1775 --- src/strands/multiagent/graph.py | 2 +- tests/strands/multiagent/test_graph.py | 33 ++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/src/strands/multiagent/graph.py b/src/strands/multiagent/graph.py index 04d158108..29a51a0bb 100644 --- a/src/strands/multiagent/graph.py +++ b/src/strands/multiagent/graph.py @@ -189,7 +189,7 @@ def reset_executor_state(self) -> None: if hasattr(self.executor, "messages"): self.executor.messages = copy.deepcopy(self._initial_messages) - if hasattr(self.executor, "state"): + if hasattr(self.executor, "state") and hasattr(self.executor.state, "get"): self.executor.state = AgentState(self._initial_state.get()) # Reset execution status diff --git a/tests/strands/multiagent/test_graph.py b/tests/strands/multiagent/test_graph.py index e978701cd..b0c9f537a 100644 --- a/tests/strands/multiagent/test_graph.py +++ b/tests/strands/multiagent/test_graph.py @@ -2476,3 +2476,36 @@ def test_find_newly_ready_nodes_only_evaluates_outbound_edges(): ready = graph._find_newly_ready_nodes([node_d]) ready_ids = {n.node_id for n in ready} assert ready_ids == {"E"}, f"Expected only E, got {ready_ids}" + +@pytest.mark.asyncio +async def test_reset_executor_state_preserves_graph_state_for_nested_graph(): + """Verify reset_executor_state does not corrupt MultiAgentBase state. + + When a GraphNode wraps a MultiAgentBase executor (e.g. a nested Graph), + reset_executor_state() must not overwrite GraphState with AgentState. + Regression test for #1775. + """ + inner_agent = create_mock_agent("inner", "inner response") + inner_builder = GraphBuilder() + inner_builder.add_node(inner_agent, "inner_node") + inner_builder.set_entry_point("inner_node") + inner_graph = inner_builder.build() + + # inner_graph.state is a GraphState, not AgentState + assert isinstance(inner_graph.state, GraphState) + + node = GraphNode(node_id="nested", executor=inner_graph) + + # Simulate a completed execution + node.execution_status = Status.COMPLETED + node.result = NodeResult(result=MagicMock(), status=Status.COMPLETED) + + # Reset should NOT corrupt the nested graph's state + node.reset_executor_state() + + # After reset, the executor's state must still be GraphState + assert isinstance(inner_graph.state, GraphState), ( + "reset_executor_state overwrote GraphState with AgentState" + ) + assert node.execution_status == Status.PENDING + assert node.result is None From 19173e7ca63dc14def2c5b22ea65f36bfc2e1c7d Mon Sep 17 00:00:00 2001 From: giulio-leone Date: Sat, 21 Mar 2026 03:35:57 +0100 Subject: [PATCH 2/2] test(graph): format nested graph reset regression Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- tests/strands/multiagent/test_graph.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/strands/multiagent/test_graph.py b/tests/strands/multiagent/test_graph.py index b0c9f537a..5ef825b39 100644 --- a/tests/strands/multiagent/test_graph.py +++ b/tests/strands/multiagent/test_graph.py @@ -2477,6 +2477,7 @@ def test_find_newly_ready_nodes_only_evaluates_outbound_edges(): ready_ids = {n.node_id for n in ready} assert ready_ids == {"E"}, f"Expected only E, got {ready_ids}" + @pytest.mark.asyncio async def test_reset_executor_state_preserves_graph_state_for_nested_graph(): """Verify reset_executor_state does not corrupt MultiAgentBase state. @@ -2504,8 +2505,6 @@ async def test_reset_executor_state_preserves_graph_state_for_nested_graph(): node.reset_executor_state() # After reset, the executor's state must still be GraphState - assert isinstance(inner_graph.state, GraphState), ( - "reset_executor_state overwrote GraphState with AgentState" - ) + assert isinstance(inner_graph.state, GraphState), "reset_executor_state overwrote GraphState with AgentState" assert node.execution_status == Status.PENDING assert node.result is None