-
Notifications
You must be signed in to change notification settings - Fork 11
[copilot-finds] Bug: resume() while-loop silently swallows exceptions from immediately-failed tasks #206
Description
Problem
In RuntimeOrchestrationContext.resume() (packages/durabletask-js/src/worker/runtime-orchestration-context.ts, lines 169-197), the isComplete while-loop does not check whether a yielded task is failed before continuing the loop. When a task is already failed (e.g., a WhenAllTask with a pre-failed child), the loop calls generator.next(this._previousTask._result) instead of generator.throw(this._previousTask._exception). Since _result is undefined on failed tasks, the generator receives undefined instead of the exception, silently losing the error.
Root Cause
The isComplete while-loop (line 170) only checks !this._previousTask.isComplete to decide whether to break. Since failed tasks have isComplete = true, the loop continues iterating and sends the (undefined) result to the generator. The isFailed check exists at the top of resume() (line 148) but is never reached inside the while-loop because the loop handles all isComplete tasks uniformly.
Reproduction Scenario
- Orchestrator schedules an activity (
task1) and a timer - Orchestrator yields the timer (not the activity)
- The activity fails while the timer is pending —
task1is nowisComplete=true, isFailed=true - The timer fires, and the generator resumes
- Generator creates
whenAll([task1])—WhenAllTaskconstructor callsonChildCompleted()for the pre-failed child, making the WhenAllTask immediately failed - Generator yields this already-failed WhenAllTask
- Bug: The while-loop sees
isComplete=true, does NOT break, and callsgenerator.next(undefined)— the failure exception is lost
Proposed Fix
Add a isFailed check inside the while-loop. When an immediately-failed task is detected, break out and delegate to the existing isFailed handling branch via recursive resume() call.
Impact
Severity: High. This bug causes silent loss of activity/sub-orchestration failure exceptions in orchestrators that use patterns like fire-and-forget activity scheduling followed by whenAll. The orchestrator receives undefined instead of the expected exception, leading to incorrect orchestration behavior and potential data loss.