Expected Behavior
yield when_all(children) should complete when all child tasks complete, even if some children finished before when_all(...) was constructed (deferred fan-in).
Common pattern:
children = [ctx.call_child_workflow(...) for ...]
yield ctx.create_timer(...) # or an activity
yield when_all(children) # constructed later
Actual Behavior
If some but not all children are already complete when when_all is constructed, when_all never completes. The orchestration hangs until an outer timeout (if any).
Root Cause
In WhenAllTask.__init__:
super().__init__(tasks) # CompositeTask counts already-complete children
self._completed_tasks = 0 # resets the counter — drops those completions
self._failed_tasks = 0
CompositeTask.__init__ correctly calls on_child_completed for pre-completed children. Resetting the counters afterward loses that count. Later completions only increment from 0, so the total never reaches len(tasks).
Edge cases today:
- 0 early completions → works
- all early completions → works (
is_complete set before reset)
- partial early completions → hangs
Steps to Reproduce
from dapr.ext.workflow._durabletask import task
children = [task.CompletableTask() for _ in range(5)]
for child in children[:3]:
child.complete(None)
all_task = task.when_all(children)
# all_task.get_completed_tasks() == 0 (bug; should be 3)
children[3].complete(None)
children[4].complete(None)
# all_task.is_complete is False forever (bug; should be True)
Observed in production stress testing against dapr-ext-workflow==1.18.1 with deferred when_any([when_all(children), timeout]) after a terminate activity — intermittent depending on how many children finish during the activity.
Release Note
RELEASE NOTE: FIX Bug causing when_all to hang when some child tasks complete before when_all is constructed
Expected Behavior
yield when_all(children)should complete when all child tasks complete, even if some children finished beforewhen_all(...)was constructed (deferred fan-in).Common pattern:
Actual Behavior
If some but not all children are already complete when
when_allis constructed,when_allnever completes. The orchestration hangs until an outer timeout (if any).Root Cause
In
WhenAllTask.__init__:CompositeTask.__init__correctly callson_child_completedfor pre-completed children. Resetting the counters afterward loses that count. Later completions only increment from 0, so the total never reacheslen(tasks).Edge cases today:
is_completeset before reset)Steps to Reproduce
Observed in production stress testing against
dapr-ext-workflow==1.18.1with deferredwhen_any([when_all(children), timeout])after a terminate activity — intermittent depending on how many children finish during the activity.Release Note
RELEASE NOTE: FIX Bug causing
when_allto hang when some child tasks complete beforewhen_allis constructed