Skip to content

Python: [Bug]: State updates not propagated to the next activity with durable function and declarative workflow #4500

@stepanradek

Description

@stepanradek

Description

Hello, we face and issue with durable function and declarative workflows.

We found that when an activity like "parse_code_1" updates a state value (e.g., Local.code), downstream activities never receive the updated value — it's always blank. This caused "evaluate_1" to always evaluate Not(IsBlank(Local.code)) as False.

- kind: ParseCode
  id: parse_code_1
  description: >
    Parses code and sets in workflow context.

- kind: If
  id: evaluate_1
  description: >
    Gate: only proceed if code parsed.
  condition: "=Not(IsBlank(Local.code))"

We traced this to _app.py in the agent framework, where the activity executor snapshots the workflow state before execution using dict(). Since dict() only creates a shallow copy, nested state objects are still shared references. When the activity mutates a nested value in place, the snapshot is silently modified too, making the before/after diff empty — so no changes are propagated forward.

about line 300...

# Deserialize shared state values to reconstruct dataclasses/Pydantic models
deserialized_state = {k: deserialize_value(v) for k, v in (shared_state_snapshot or {}).items()}
original_snapshot = dict(deserialized_state)

In our testing, replacing the shallow copy with copy.deepcopy() resolved the issue. However, we'd like the framework owners to review and determine the appropriate fix, as there may be broader implications we're not aware of.

# Deserialize shared state values to reconstruct dataclasses/Pydantic models
deserialized_state = {k: deserialize_value(v) for k, v in (shared_state_snapshot or {}).items()}
original_snapshot = copy.deepcopy(deserialized_state)

Thank you for having look into it!

Code Sample

Following code is working with fix described above.

ParseCodeExecutor(DeclarativeActionExecutor):
...
state.set("Local.code", "SOMECODEXXX")
code_check = state.get("Local.code")
logger.warning("[ParseCode] STATE-DEBUG: Local.code after set = %r", code_check)

CodeReaderExecutor(DeclarativeActionExecutor):
...
code_check = state.get("Local.code")
logger.warning("[CodeReader] STATE-DEBUG: Local.code after set = %r", code_check)

Error Messages / Stack Traces

Package Versions

agent-framework-azurefunctions>=1.0.0b260225; agent-framework-declarative>=1.0.0b260225;agent-framework-durabletask>=1.0.0b260225

Python Version

Python 3.12

Additional Context

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions