Skip to content

fix(graph): prevent reset_executor_state from corrupting MultiAgentBase state#1896

Open
giulio-leone wants to merge 1 commit intostrands-agents:mainfrom
giulio-leone:fix/graphnode-reset-executor-state
Open

fix(graph): prevent reset_executor_state from corrupting MultiAgentBase state#1896
giulio-leone wants to merge 1 commit intostrands-agents:mainfrom
giulio-leone:fix/graphnode-reset-executor-state

Conversation

@giulio-leone
Copy link
Contributor

Summary

Fixes #1775GraphNode.reset_executor_state() corrupts MultiAgentBase executor state by overwriting GraphState with AgentState.

Root Cause

reset_executor_state() checks hasattr(self.executor, 'state') without verifying the state type:

# Before (broken)
if hasattr(self.executor, 'state'):
    self.executor.state = AgentState(self._initial_state.get())  # Overwrites GraphState!

__post_init__ already had the correct guard (hasattr(self.executor.state, 'get')), but reset_executor_state() was missing it.

Impact

Two call sites affected:

Call site Trigger
_execute_node reset_on_revisit=True with nested graph in a cycle
deserialize_state Always — iterates all nodes when restoring a completed run

Fix

Mirror the __post_init__ guard:

# After (fixed)
if hasattr(self.executor, 'state') and hasattr(self.executor.state, 'get'):
    self.executor.state = AgentState(self._initial_state.get())

Test

Added test_reset_executor_state_preserves_graph_state_for_nested_graph — creates a GraphNode with a nested Graph executor and verifies reset_executor_state() preserves the GraphState type.

All 49 graph tests pass.

@giulio-leone
Copy link
Contributor Author

Friendly ping — prevents reset_executor_state from corrupting MultiAgentBase state by preserving agent-level state that should persist across executor resets.

…se 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 strands-agents#1775
@giulio-leone giulio-leone force-pushed the fix/graphnode-reset-executor-state branch from 3537416 to 9f939fd Compare March 19, 2026 11:01
@github-actions github-actions bot added size/s and removed size/s labels Mar 19, 2026
@giulio-leone
Copy link
Contributor Author

Rebased this branch onto the latest main and force-pushed it. The only manual rebase resolution was in tests/strands/multiagent/test_graph.py, where I kept both the new upstream test and this PR's regression test.

Local validation I ran:

  • .venv/bin/python -m pytest tests/strands/multiagent/test_graph.py::test_reset_executor_state_preserves_graph_state_for_nested_graph -q -> 1 passed

Real branch-vs-main proof (using the repo venv and explicit PYTHONPATH=<checkout>/src so the imported module path is unambiguous):

  • main imported sdk-python-main/src/strands/multiagent/graph.py
    • before reset: GraphState
    • after reset: JSONSerializableDict
    • node status reset to PENDING, result cleared
  • this branch imported sdk-python/src/strands/multiagent/graph.py
    • before reset: GraphState
    • after reset: GraphState
    • node status reset to PENDING, result cleared

So the rebased branch still fixes the real corruption path from #1775: nested graph executor state no longer gets overwritten to the wrong state type during reset_executor_state().

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] GraphNode.reset_executor_state() corrupts MultiAgentBase executor state

1 participant