-
Notifications
You must be signed in to change notification settings - Fork 10
Description
Problem
RuntimeOrchestrationContext.run() does not validate that the first value yielded by an orchestrator generator is a Task instance. The resume() method (which processes subsequent yields) already validates this with an instanceof Task check and throws a clear error:
// In resume() — line 184
if (!(value instanceof Task)) {
throw new Error("The orchestrator generator yielded a non-Task object");
}However, run() (which processes the first yield) silently accepts any value — null, undefined, plain objects, or primitives — and assigns it to _previousTask without validation. There was even a TODO comment acknowledging this gap: "// TODO: check if the task is null?".
File: packages/durabletask-js/src/worker/runtime-orchestration-context.ts, lines 109–132
Root Cause
The run() method was written without the same validation guard that resume() has. When the generator yields a non-Task value on its first yield:
_previousTaskis set to the non-Task value (e.g.,null,42,{foo: "bar"})- The
instanceof Taskcheck on theisCompleteguard fails silently — no error, just skips - Later, when
resume()is called by a completion event, it checks_previousTask.isFailedand_previousTask.isComplete— both areundefinedon non-Task values - The generator is never advanced, and the orchestration hangs indefinitely with no error
Impact
- Severity: Medium — causes orchestrations to hang silently with no error message
- Affected scenarios: Any orchestrator that accidentally yields a non-Task value on its first yield (e.g., a raw Promise,
null, or forgetting to call a context method that returns a Task) - Debugging difficulty: High — no error is thrown, no log is emitted; the orchestration simply stops progressing