conductor-oss / conductor-oss/go-sdk
DynamicForkTask.getJoinTask() always ignores stored join — NewDynamicForkWithJoinTask() misleading API
- 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