conductor-oss / conductor-oss/go-sdk

DynamicForkTask.getJoinTask() always ignores stored join — NewDynamicForkWithJoinTask() misleading API

Open Beginner friendly
#264 0 comments 0 reactions 0 assignees View on GitHub
bug
Dominant language
Go
Stars
66
Forks
25
Avg merge
3h 44m
Merged PRs (30d)
2

Description

## Summary

`DynamicForkTask.getJoinTask()` always creates a brand-new `JoinTask` and ignores the `task.join` field, even when the task was constructed via `NewDynamicForkWithJoinTask()`. The stored join (with user-specified `joinOn`) is permanently discarded — the result is an empty `joinOn`, and the JOIN completes immediately.

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

## Root Cause

```go
// sdk/workflow/fork_join_dynamic.go

// NewDynamicForkWithJoinTask stores a join in task.join...
func NewDynamicForkWithJoinTask(taskRefName string, join *JoinTask, ...) *DynamicForkTask {
...
task.join = join
return task
}

// ...but getJoinTask() never reads it:
func (task *DynamicForkTask) getJoinTask() model.WorkflowTask {
join := NewJoinTask(task.taskReferenceName + "_join") // always creates new, ignores task.join
return (join.toWorkflowTask())[0]
}
```

Regardless of whether the user called `NewDynamicForkTask()` or `NewDynamicForkWithJoinTask()`, the emitted JOIN always has empty `joinOn: []`.

## Impact

Every `FORK_JOIN_DYNAMIC` built with the Go SDK joins immediately, before the dynamic tasks finish. This is the same silent-completion bug as conductor-oss/go-sdk#263 (static FORK_JOIN), but worse because the user is given an explicit API (`NewDynamicForkWithJoinTask`) that implies they can control the join — but that control is completely ignored.

## Fix

```go
func (task *DynamicForkTask) getJoinTask() model.WorkflowTask {
if task.join != nil {
return (task.join.toWorkflowTask())[0]
}
// Dynamic fork: no auto-detection possible (tasks are runtime-determined)
// Return a join with empty joinOn and document that user must supply one
return (NewJoinTask(task.taskReferenceName + "_join")).toWorkflowTask()[0]
}
```

The check `task.join != nil` ensures that when the user provides a join via `NewDynamicForkWithJoinTask()`, it is actually used.

For dynamic forks, the server determines the set of forked tasks at runtime, so there is no general way to auto-infer `joinOn` the way static `ForkTask` can. Users should supply an explicit join.

Contributor guide

No contributing guide indexed for this repository

Research direction

Start in sdk/workflow/fork_join_dynamic.go by reading DynamicForkTask.getJoinTask(), NewDynamicForkTask(), and NewDynamicForkWithJoinTask(). Ensure a supplied task.join is emitted for the JOIN, while the existing empty join remains the fallback for dynamic forks. Done means the stored joinOn is preserved when using NewDynamicForkWithJoinTask().

Written by the indexing model from the issue text.

Assessment

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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.