Skip to content

[copilot-finds] Bug: resume() while-loop silently swallows exceptions from immediately-failed tasks #206

@github-actions

Description

@github-actions

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

  1. Orchestrator schedules an activity (task1) and a timer
  2. Orchestrator yields the timer (not the activity)
  3. The activity fails while the timer is pending — task1 is now isComplete=true, isFailed=true
  4. The timer fires, and the generator resumes
  5. Generator creates whenAll([task1])WhenAllTask constructor calls onChildCompleted() for the pre-failed child, making the WhenAllTask immediately failed
  6. Generator yields this already-failed WhenAllTask
  7. Bug: The while-loop sees isComplete=true, does NOT break, and calls generator.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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    copilot-findsFindings from daily automated code review agent

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions