Skip to content

fix(workflow): deep copy initial_state to prevent mutation leaks across runs#21780

Merged
logan-markewich merged 1 commit into
run-llama:mainfrom
substrai:fix/agent-workflow-initial-state-leak
May 28, 2026
Merged

fix(workflow): deep copy initial_state to prevent mutation leaks across runs#21780
logan-markewich merged 1 commit into
run-llama:mainfrom
substrai:fix/agent-workflow-initial-state-leak

Conversation

@gauravSsinha

Copy link
Copy Markdown
Contributor

Description

Fixes AgentWorkflow leaking initial_state mutations across run() calls when the same workflow instance is reused.

Problem

  • Multi-agent path (AgentWorkflow): stores self.initial_state in the context by direct reference, so in-place mutations modify the workflow's own state
  • Single-agent path (BaseWorkflowAgent): uses .copy() (shallow copy), which avoids top-level leakage but still leaks nested mutable values (lists, dicts)

This violates the documented behavior that workflows are stateless between runs unless a Context is explicitly reused.

Fix

Use copy.deepcopy() in both paths to ensure each run() gets a fully independent copy of initial_state.

Changes

  • multi_agent_workflow.py: copy.deepcopy(self.initial_state) instead of self.initial_state
  • base_agent.py: copy.deepcopy(self.initial_state) instead of self.initial_state.copy()

Reproduction (from issue)

workflow = AgentWorkflow.from_tools_or_functions(
    [bump_counter], llm=MockFunctionCallingLLM(),
    initial_state={"counter": 0},
)
await workflow.run(user_msg="bump it")  # counter -> 1
state = await (await workflow.run(user_msg="bump it")).ctx.store.get("state")
# Before fix: {'counter': 2}  (leaked from first run)
# After fix:  {'counter': 1}  (independent copy)

Fixes #21774

…ss runs

AgentWorkflow stores initial_state in the context by reference (multi-agent
path) or via shallow copy (single-agent path). This causes in-place state
mutations during one run() to persist into subsequent runs, violating the
documented stateless-between-runs behavior.

Fix: use copy.deepcopy() in both paths to ensure each run gets an independent
copy of initial_state, preventing nested mutable values (lists, dicts) from
leaking across invocations.

Fixes run-llama#21774

Signed-off-by: Gaurav Kumar Sinha <gaurav@substrai.dev>
@dosubot dosubot Bot added the size:XS This PR changes 0-9 lines, ignoring generated files. label May 26, 2026
@dosubot dosubot Bot added the lgtm This PR has been approved by a maintainer label May 28, 2026
@logan-markewich logan-markewich merged commit dc8642b into run-llama:main May 28, 2026
10 of 11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

lgtm This PR has been approved by a maintainer size:XS This PR changes 0-9 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: AgentWorkflow leaks initial_state mutations across runs

2 participants