Problem
In packages/durabletask-js/src/worker/runtime-orchestration-context.ts, the resume() method has inconsistent validation between its success and failure recovery paths.
When an orchestrator generator catches a failed task exception and then yields a new value, the success path (line 184-186) correctly validates that the yielded value is a Task instance and throws "The orchestrator generator yielded a non-Task object" if not. However, the failure recovery path (line 158-166) lacks this validation entirely — it silently returns when the yielded value is not a Task.
Root Cause
The failure recovery path in resume() only checks if (throwResult.value instanceof Task) and handles the positive case, but has no else branch to reject non-Task values. This was likely an oversight when the validation was added to the success path but not the failure path.
Impact
When an orchestrator catches a task failure and yields a non-Task value (a programming error by the user), the orchestration enters a broken state:
_previousTask still references the old failed task
- On the next
resume(), the same exception is re-thrown to the generator
- The generator has already moved past that point, producing confusing behavior
- The orchestration may get stuck or produce misleading error messages
With the success path, the same programming error produces a clear, actionable error message.
Severity: Medium — affects error reporting and debugging experience; does not cause data loss.
Affected scenarios: Orchestrators that use try/catch around yield statements and have a code path that yields a non-Task value after catching.
Proposed Fix
Add the same throw new Error("The orchestrator generator yielded a non-Task object") statement after the if (throwResult.value instanceof Task) block in the failure recovery path, making it consistent with the success path.
Problem
In
packages/durabletask-js/src/worker/runtime-orchestration-context.ts, theresume()method has inconsistent validation between its success and failure recovery paths.When an orchestrator generator catches a failed task exception and then yields a new value, the success path (line 184-186) correctly validates that the yielded value is a
Taskinstance and throws"The orchestrator generator yielded a non-Task object"if not. However, the failure recovery path (line 158-166) lacks this validation entirely — it silently returns when the yielded value is not aTask.Root Cause
The failure recovery path in
resume()only checksif (throwResult.value instanceof Task)and handles the positive case, but has noelsebranch to reject non-Task values. This was likely an oversight when the validation was added to the success path but not the failure path.Impact
When an orchestrator catches a task failure and yields a non-Task value (a programming error by the user), the orchestration enters a broken state:
_previousTaskstill references the old failed taskresume(), the same exception is re-thrown to the generatorWith the success path, the same programming error produces a clear, actionable error message.
Severity: Medium — affects error reporting and debugging experience; does not cause data loss.
Affected scenarios: Orchestrators that use try/catch around
yieldstatements and have a code path that yields a non-Task value after catching.Proposed Fix
Add the same
throw new Error("The orchestrator generator yielded a non-Task object")statement after theif (throwResult.value instanceof Task)block in the failure recovery path, making it consistent with the success path.