conductor-oss / conductor-oss/csharp-sdk

DynamicFork auto-join has empty joinOn — JOIN task completes immediately without waiting for dynamic branches

Open
#158 0 comments 0 reactions 0 assignees View on GitHub
bug
Dominant language
C#
Stars
54
Forks
23
Avg merge
4d 21h
Merged PRs (30d)
2

Description

## Summary

`DynamicFork`'s companion `JoinTask` is constructed with zero arguments, producing empty `joinOn: []`. When added to a workflow, the JOIN task completes immediately — before any dynamic fork branches finish.

Tested against: **Conductor OSS 3.32.0-rc.9**

## Root Cause

```csharp
// Conductor/Definition/TaskType/DynamicFork.cs:32
public DynamicFork(string taskReferenceName, ...) : base(...)
{
this.Join = new JoinTask(taskReferenceName + "_join"); // no joinOn args
...
}
```

`JoinTask(string taskReferenceName, params WorkflowTask[] joinOn)` — called with zero `WorkflowTask` args → `JoinOn = new List()` on the companion join task.

When the user adds `dynamicFork.Join` to the workflow definition, the JOIN task has `joinOn: []`. The server's Join executor evaluates `joinOn.stream().allMatch(...)` — on an empty stream, `allMatch` returns `true` immediately — the JOIN transitions to `COMPLETED` without waiting for any dynamic fork branches.

## Impact

Any workflow that uses `DynamicFork.Join` to obtain the join task will have a join that completes immediately. Dynamic tasks spawned by the fork run in the background while the rest of the workflow proceeds as if the fork were done.

## Cross-SDK Confirmation

Same root cause as:
- [javascript-sdk#135](https://github.com/conductor-oss/javascript-sdk/issues/135)
- [go-sdk#264](https://github.com/conductor-oss/go-sdk/issues/264)

The server behavior (empty `joinOn` → immediate completion) was live-confirmed against Conductor OSS 3.32.0-rc.9 in those audits.

## Fix

Require callers to supply the join-on tasks, or expose it as a settable property that must be populated before use:

```csharp
public DynamicFork(string taskReferenceName, string forkTasksParameter,
string forkTasksInputsParameter, params WorkflowTask[] joinOn)
{
this.Join = new JoinTask(taskReferenceName + "_join", joinOn);
...
}
```

For dynamic forks, the tasks are determined at runtime, so the client cannot auto-infer `joinOn`. The user must explicitly pass the tasks to wait for (typically the dynamic task reference names they configure at workflow design time).

Contributor guide

No contributing guide indexed for this repository

Research direction

Start in Conductor/Definition/TaskType/DynamicFork.cs at the constructor around line 32, then inspect the JoinTask constructor and how DynamicFork.Join is added to a workflow. Ensure the companion JOIN receives the caller-supplied tasks and verify that its serialized joinOn is populated so it waits for the dynamic branches.

Written by the indexing model from the issue text.

Assessment

Tech stack
csharp
Domain
backend-api-design
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.