Skip to content

Python: Preserve workflow run kwargs when continuing with run(responses=...)#4296

Merged
moonbox3 merged 2 commits intomicrosoft:mainfrom
moonbox3:agent/fix-4293-3
Feb 26, 2026
Merged

Python: Preserve workflow run kwargs when continuing with run(responses=...)#4296
moonbox3 merged 2 commits intomicrosoft:mainfrom
moonbox3:agent/fix-4293-3

Conversation

@moonbox3
Copy link
Contributor

Motivation and Context

When a workflow pauses (e.g., for human approval) and is resumed via run(responses=...), the original run_kwargs passed in the initial call were silently dropped and replaced with an empty dict. This caused executors/agents to lose access to custom data like auth tokens or configuration passed at run time.

Fixes #4293

Description

The root cause was that _state.set(WORKFLOW_RUN_KWARGS_KEY, run_kwargs or {}) unconditionally overwrote the stored kwargs on every run() call, including continuation calls where run_kwargs is None. The fix conditionally updates stored kwargs: they are only overwritten when new kwargs are explicitly provided, or when the context is being reset (fresh run). On continuation calls with no new kwargs (reset_context=False), the previously stored kwargs are preserved. Two regression tests verify both preservation and explicit override behavior.

Contribution Checklist

  • The code builds clean without any errors or warnings
  • The PR follows the Contribution Guidelines
  • All unit tests pass, and I have added new tests where possible
  • Is this a breaking change? If yes, add "[BREAKING]" prefix to the title of the PR.

Note: PR autogenerated by moonbox3's agent

…icrosoft#4293)

When continuing a paused workflow with run(responses=...), the existing
run kwargs stored in state were unconditionally overwritten with an empty
dict. This caused subsequent agent invocations to lose the original run
context (e.g., custom_data, user tokens).

Now kwargs are only overwritten when:
- New kwargs are explicitly provided (override), or
- State was just cleared for a fresh run (initialize to {})

On continuation without new kwargs, existing kwargs are preserved.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings February 26, 2026 06:00
Copy link
Contributor Author

@moonbox3 moonbox3 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated Code Review

Reviewers: 3 | Confidence: 85%

✓ Correctness

The change correctly fixes the kwargs-overwrite-on-continuation bug (#4293) by guarding the state write: kwargs are only written when explicitly provided or when the context is freshly reset. The logic is sound for the intended workflow lifecycle (initial run → pause → continue). The tests cover preservation and override scenarios well. One minor edge-case worth noting: if run(responses=..., reset_context=False) is ever called without a preceding run, WORKFLOW_RUN_KWARGS_KEY will never be set, whereas the old code always ensured the key existed. This is unlikely in practice since reset_context=False implies a continuation, but the old comment explicitly called out deterministic retrieval.

✓ Security Reliability

This diff fixes a bug where workflow run kwargs were unintentionally overwritten to an empty dict on continuation calls (reset_context=False). The new conditional logic correctly preserves kwargs when no new ones are provided during continuation, and only resets them on a fresh run. The change is minimal, well-scoped, and accompanied by thorough regression tests. No security or reliability issues are introduced.

✓ Test Coverage

The two new tests directly cover the primary behavioral change (preserving kwargs on continuation and overriding them when explicitly provided). Assertions are meaningful and include a diagnostic message on the most important check. However, an edge case is missing: passing an explicit empty dict (run_kwargs={}) on continuation should overwrite the preserved kwargs with {} (since {} is not None), which is a subtle but important distinction from passing None. There is also no dedicated test for the reset_context=True with run_kwargs=None branch (line 355), though existing tests for fresh runs likely exercise it indirectly. Overall the coverage is solid for the primary scenarios.

Suggestions

  • Consider whether downstream consumers of WORKFLOW_RUN_KWARGS_KEY use a safe access pattern (e.g., state.get with a default) rather than assuming the key always exists. The old code guaranteed the key was always set; the new code may leave it unset on the (unlikely) path where reset_context=False and run_kwargs=None with no prior run.
  • Consider whether the state should be defensively initialized with an empty dict if WORKFLOW_RUN_KWARGS_KEY is missing during a continuation (reset_context=False, run_kwargs=None) — e.g., if a workflow is resumed from persisted state that predates this key. A missing key at retrieval time could surface as an unexpected KeyError downstream.
  • Add a test for the continuation case where run_kwargs is explicitly an empty dict {} (not None). This exercises a subtle boundary: {} passes the is not None check and should clear previously-stored kwargs, whereas omitting kwargs (None) should preserve them.
  • Consider adding a targeted unit test that calls workflow.run(...) with reset_context=True and no kwargs to explicitly verify the elif reset_context branch (line 355) stores {}.

Automated review by moonbox3's agents

@github-actions github-actions bot changed the title Preserve workflow run kwargs when continuing with run(responses=...) Python: Preserve workflow run kwargs when continuing with run(responses=...) Feb 26, 2026
@markwallace-microsoft
Copy link
Member

markwallace-microsoft commented Feb 26, 2026

Python Test Coverage

Python Test Coverage Report •
FileStmtsMissCoverMissing
packages/core/agent_framework/_workflows
   _agent_executor.py2002687%97, 113, 168–169, 221–222, 224–225, 255–257, 265–267, 275–277, 279, 283, 287, 291–292, 391–392, 438, 456
   _workflow.py2671992%87, 268–270, 272–273, 291, 295, 430, 618, 639, 695, 707, 713, 718, 738–740, 753
   _workflow_executor.py1783083%94, 444, 467, 469, 477–478, 483, 485, 490, 492, 545, 573–579, 583–585, 593, 598, 609, 619, 623, 629, 633, 643, 647
TOTAL22177276287% 

Python Unit Test Overview

Tests Skipped Failures Errors Time
4676 247 💤 0 ❌ 0 🔥 1m 17s ⏱️

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a regression where workflow run kwargs were incorrectly cleared when continuing a paused workflow via run(responses=...). The issue affected executors/agents that rely on custom data (e.g., auth tokens, configuration) passed at runtime.

Changes:

  • Modified kwargs storage logic to preserve existing kwargs on continuation calls unless explicitly overridden
  • Added two comprehensive regression tests validating both preservation and override behaviors

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
python/packages/core/agent_framework/_workflows/_workflow.py Replaced unconditional kwargs overwrite with conditional logic: preserve on continuation (reset_context=False) unless new kwargs explicitly provided
python/packages/core/tests/workflow/test_workflow_kwargs.py Added test_kwargs_preserved_on_response_continuation and test_kwargs_overridden_on_response_continuation to verify fix

- Use consistent get_state(key, {}) default pattern in _agent_executor.py
  and _workflow_executor.py instead of get_state(key) or {} to safely
  handle missing WORKFLOW_RUN_KWARGS_KEY
- Add test for empty-value kwargs on continuation (custom_data={}) to
  verify the is-not-None boundary between overwrite and preserve
- Add test for reset_context=True with no kwargs to exercise the elif
  branch that initializes WORKFLOW_RUN_KWARGS_KEY to {}
- Add len assertion to override test for consistency
- Document kwargs-collapsing behavior at the public API call site

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@moonbox3 moonbox3 enabled auto-merge February 26, 2026 09:04
@moonbox3 moonbox3 self-assigned this Feb 26, 2026
@moonbox3 moonbox3 moved this to In Progress in Agent Framework Feb 26, 2026
@moonbox3 moonbox3 moved this from In Progress to In Review in Agent Framework Feb 26, 2026
@moonbox3 moonbox3 added this pull request to the merge queue Feb 26, 2026
Merged via the queue into microsoft:main with commit b46fe1c Feb 26, 2026
29 checks passed
@github-project-automation github-project-automation bot moved this from In Review to Done in Agent Framework Feb 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

Python: Workflow run kwargs are dropped when continuing with run(responses=...)

5 participants