Summary
forkTaskJoin() creates a JOIN task with joinOn: [] (empty array). The Conductor
server's JOIN executor uses joinOn.stream().allMatch(...) to check if all listed tasks
are terminal — on an empty stream, allMatch short-circuits to true immediately,
so the JOIN transitions to COMPLETED without waiting for any fork branches.
Server baseline
Conductor 3.32.0-rc.9
Root cause
// forkJoin.ts:22
export const forkTaskJoin = (
taskReferenceName: string,
forkTasks: TaskDefTypes[],
optional?: boolean
): [ForkJoinTaskDef, JoinTaskDef] => [
forkTask(taskReferenceName, forkTasks),
generateJoinTask({ name: `${taskReferenceName}_join`, optional }),
// ^^^ no joinOn passed — defaults to []
];
generateJoinTask defaults to joinOn: []. The existing test (factory.test.ts:141)
explicitly asserts joinOn: [], locking in the broken behavior.
Live evidence
Tested against Conductor OSS 3.32.0-rc.9 with a 10-second WAIT task as the fork branch:
After 2 seconds:
forkTaskJoin (joinOn=[]) → join=COMPLETED wait_branch=IN_PROGRESS ← BUG
manual joinOn=['wait_branch'] → join=IN_PROGRESS wait_branch=IN_PROGRESS ← CORRECT
The JOIN with joinOn: [] reached COMPLETED while the fork branch was still running.
Impact
Any workflow that uses forkTaskJoin() will have a JOIN that completes immediately
when first evaluated. Fork branches continue running concurrently, but the downstream
workflow does not wait for them.
Proposed fix
Infer joinOn from the last task reference name in the fork branch:
export const forkTaskJoin = (
taskReferenceName: string,
forkTasks: TaskDefTypes[],
optional?: boolean
): [ForkJoinTaskDef, JoinTaskDef] => {
const joinOn = forkTasks.length > 0
? [forkTasks[forkTasks.length - 1].taskReferenceName]
: [];
return [
forkTask(taskReferenceName, forkTasks),
generateJoinTask({ name: `${taskReferenceName}_join`, joinOn, optional }),
];
};
factory.test.ts should also be updated to assert joinOn: ['forkTaskJoin'] (the last
task's ref name) rather than joinOn: [].
Related
Discovered during systematic SDK audit against Conductor OSS 3.32.0-rc.9.
The multi-branch limitation of forkTask() is separately tracked in #94.
Summary
forkTaskJoin()creates a JOIN task withjoinOn: [](empty array). The Conductorserver's JOIN executor uses
joinOn.stream().allMatch(...)to check if all listed tasksare terminal — on an empty stream,
allMatchshort-circuits totrueimmediately,so the JOIN transitions to COMPLETED without waiting for any fork branches.
Server baseline
Conductor 3.32.0-rc.9
Root cause
generateJoinTaskdefaults tojoinOn: []. The existing test (factory.test.ts:141)explicitly asserts
joinOn: [], locking in the broken behavior.Live evidence
Tested against Conductor OSS 3.32.0-rc.9 with a 10-second WAIT task as the fork branch:
The JOIN with
joinOn: []reached COMPLETED while the fork branch was still running.Impact
Any workflow that uses
forkTaskJoin()will have a JOIN that completes immediatelywhen first evaluated. Fork branches continue running concurrently, but the downstream
workflow does not wait for them.
Proposed fix
Infer
joinOnfrom the last task reference name in the fork branch:factory.test.tsshould also be updated to assertjoinOn: ['forkTaskJoin'](the lasttask's ref name) rather than
joinOn: [].Related
Discovered during systematic SDK audit against Conductor OSS 3.32.0-rc.9.
The multi-branch limitation of
forkTask()is separately tracked in #94.