fix: skip AgentState reset for MultiAgentBase executors in GraphNode#1791
fix: skip AgentState reset for MultiAgentBase executors in GraphNode#1791giulio-leone wants to merge 2 commits intostrands-agents:mainfrom
Conversation
GraphNode.reset_executor_state() unconditionally overwrites executor.state with AgentState(...), corrupting the GraphState dataclass on nested Graph/Swarm executors. This breaks: - Cyclic graphs with reset_on_revisit enabled (nested graph revisit) - Session persistence deserialization of completed graph runs Add isinstance(self.executor, MultiAgentBase) guard to skip the state reset for multi-agent executors whose state is not an AgentState dict. Fixes strands-agents#1775 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Fixes a state-type corruption bug in GraphNode.reset_executor_state() where nested multi-agent executors (e.g., Graph/Swarm) could have their dataclass state overwritten with an AgentState during node resets and session deserialization.
Changes:
- Skip resetting
executor.statewhen the executor is aMultiAgentBase, preservingGraphState/SwarmStatetypes. - Add a regression test ensuring
reset_executor_state()does not overwrite a multi-agent executor’s state type. - Expand method docstring to clarify why multi-agent state resets are skipped.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
src/strands/multiagent/graph.py |
Adds MultiAgentBase guard in GraphNode.reset_executor_state() to prevent clobbering nested executor state types. |
tests/strands/multiagent/test_graph.py |
Adds regression test for #1775 validating multi-agent state type is preserved across resets. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| node.result = NodeResult( | ||
| result="test", |
There was a problem hiding this comment.
NodeResult.result is defined as AgentResult | MultiAgentResult | Exception, but this test sets it to a plain string. This can make the test brittle (e.g., any future code path calling NodeResult.to_dict()/get_agent_results() would fail) and doesn’t reflect real usage. Consider constructing a minimal AgentResult (or MultiAgentResult) here instead of a string.
| node.result = NodeResult( | |
| result="test", | |
| agent_result = AgentResult( | |
| message={"role": "assistant", "content": [{"text": "test"}]}, | |
| stop_reason="end_turn", | |
| state={}, | |
| metrics=None, | |
| ) | |
| node.result = NodeResult( | |
| result=agent_result, |
There was a problem hiding this comment.
Fixed in 52810a0 — replaced plain string with proper AgentResult instance matching the type annotation.
…aph_state Replace plain string with AgentResult instance for NodeResult.result to match the type annotation (AgentResult | MultiAgentResult | Exception) and prevent future breakage from type-checking code paths. Refs: strands-agents#1791
|
All CI checks pass. Ready for review. |
Problem
GraphNode.reset_executor_state()unconditionally overwritesexecutor.statewithAgentState(...), corrupting theGraphStatedataclass on nestedGraph/Swarmexecutors.Two call sites are affected:
_execute_node(guarded byreset_on_revisit): triggers when a nested graph node is revisited in a cycledeserialize_state(unguarded): iterates all nodes unconditionally when restoring a completed graph runRoot Cause
__post_init__correctly guards against capturingMultiAgentBasestate (hasattr(self.executor.state, "get")), so_initial_statestays as the default emptyAgentState(). Butreset_executor_statelacks this guard:Fix
Add
isinstance(self.executor, MultiAgentBase)guard to skip the state reset for multi-agent executors whose state is not anAgentStatedict:Test
Added
test_reset_executor_state_preserves_multiagent_state_typethat:MultiAgentBasemock with aGraphStateattributereset_executor_state()GraphStateinstance (not overwritten toAgentState)Fixes #1775