Skip to content

Guard sdk-java from NoSuchElementException - #1594

Open
mcruzdev wants to merge 3 commits into
open-workflow-specification:mainfrom
mcruzdev:issue-1593
Open

Guard sdk-java from NoSuchElementException#1594
mcruzdev wants to merge 3 commits into
open-workflow-specification:mainfrom
mcruzdev:issue-1593

Conversation

@mcruzdev

@mcruzdev mcruzdev commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator

Many thanks for submitting your Pull Request ❤️!

What this PR does / why we need it:

Special notes for reviewers:

Additional information (if needed):

Closes #1593

Signed-off-by: Matheus Cruz <matheuscruz.dev@gmail.com>
@mcruzdev
mcruzdev requested a review from fjtirado as a code owner August 3, 2026 23:38
Copilot AI review requested due to automatic review settings August 3, 2026 23:38

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses issue #1593 by preventing workflow definition creation/execution from crashing when a workflow has an empty (or null) top-level task list.

Changes:

  • Guard TaskExecutorHelper.createExecutorList(...) against null/empty task lists to avoid NoSuchElementException.
  • Update workflow execution startup to gracefully handle a null start task (i.e., “no tasks”) and complete with the (filtered) input.
  • Add unit tests asserting that an empty/null top-level task list results in a null start task.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
impl/core/src/main/java/io/serverlessworkflow/impl/executors/TaskExecutorHelper.java Adds an early-return guard for null/empty task lists to prevent iterator .next() crashes.
impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowMutableInstance.java Introduces a safe execution path when the workflow has no start task, completing immediately with the input.
impl/core/src/test/java/io/serverlessworkflow/impl/TaskExecutorHelperTest.java Adds regression tests for empty and null top-level workflow task lists.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +72 to +74
if (taskItems == null || taskItems.isEmpty()) {
return null;
}
Comment on lines +113 to +126
private CompletableFuture<WorkflowModel> processTaskList(WorkflowContext workflowContext) {
WorkflowModel inputModel =
workflowContext
.definition()
.inputFilter()
.map(f -> f.apply(workflowContext, null, input))
.orElse(input);
TaskExecutor<?> startTask = workflowContext.definition().startTask();
if (startTask == null) {
return CompletableFuture.completedFuture(inputModel);
}
return TaskExecutorHelper.processTaskList(
startTask, workflowContext, Optional.empty(), inputModel);
}
Signed-off-by: Matheus Cruz <matheuscruz.dev@gmail.com>
Copilot AI review requested due to automatic review settings August 4, 2026 00:02

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.

Suppressed comments (2)

impl/core/src/test/java/io/serverlessworkflow/impl/TaskExecutorHelperTest.java:55

  • The fix relies on the runtime path where a workflow has no tasks (i.e., startTask() is null) completing successfully via the new null-guard in TaskExecutorHelper.processTaskList. The current tests only assert startTask() is null; consider also asserting that starting the workflow completes as a no-op and returns the original input model, so this regression is covered end-to-end.
  @Test
  void emptyTaskListShouldProduceNullStartTask() {
    Workflow workflow = new Workflow();
    workflow.setDocument(new Document().withDsl("1.0.0").withName("empty").withVersion("0.1.0"));
    workflow.setDo(Collections.emptyList());

    WorkflowModelFactory modelFactory = Mockito.mock(WorkflowModelFactory.class);
    try (WorkflowApplication app =
        WorkflowApplication.builder().withModelFactory(modelFactory).build()) {
      WorkflowDefinition definition = app.workflowDefinition(workflow);
      assertThat(definition.startTask()).isNull();
    }
  }

impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowMutableInstance.java:116

  • The new helper method parameter workflowContext shadows the instance field of the same name, which makes it easy to accidentally reference the wrong one in future edits. Renaming the parameter (e.g., to context) improves readability without changing behavior.
  private CompletableFuture<WorkflowModel> processTaskList(WorkflowContext workflowContext) {
    WorkflowModel inputModel =
        workflowContext
            .definition()
            .inputFilter()

Signed-off-by: Matheus Cruz <matheuscruz.dev@gmail.com>
Copilot AI review requested due to automatic review settings August 4, 2026 00:48

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.

Suppressed comments (2)

impl/core/src/test/java/io/serverlessworkflow/impl/TaskExecutorHelperTest.java:43

  • Test name refers to a "TaskList", but the behavior under test is specifically a null workflow do list (i.e., workflow.setDo(null)). Renaming improves clarity and avoids ambiguity about which list is being tested.
  void nullTaskListShouldProduceNullStartTask() {

impl/core/src/test/java/io/serverlessworkflow/impl/TaskExecutorHelperTest.java:29

  • Test name refers to a "TaskList", but the behavior under test is specifically an empty workflow do list (i.e., workflow.setDo(Collections.emptyList())). Renaming improves clarity and makes it easier to correlate with the workflow property involved.

This issue also appears on line 43 of the same file.

  void emptyTaskListShouldProduceNullStartTask() {

@fjtirado fjtirado left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmmm, I need a yaml reproducer for this one. If there is not such reproducer, then the fix should be in the fluent dsl cpde, to prevent a not valid workflow definition to be generated rather that adding defensive checks in the implementation code

If, as I suspected, there is not way that the YAML file is validated with an empty task list, then the solution should be to do a check in the build method of the fluent DSL to prevent that. Or, in other word, there should be not workflow object created with empty task list. And it is is, it is perfectly ok for the implementation code to throw any unexpected exception, because the workflow object is not a valid one.

@mcruzdev

mcruzdev commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator Author

Or, in other word, there should be not workflow object created with empty task list. And it is is, it is perfectly ok for the implementation code to throw any unexpected exception, because the workflow object is not a valid one.

I prefer to throw an exception centralizing the validation and additionally to create an empty list when there is no task added while using the DSL.

@fjtirado

fjtirado commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

Or, in other word, there should be not workflow object created with empty task list. And it is is, it is perfectly ok for the implementation code to throw any unexpected exception, because the workflow object is not a valid one.

I prefer to throw an exception centralizing the validation

Im not fan of double validation (which is what you are doing in this PR, because the yaml is already validated) this "validation" should be done in the DSL.

Comment on lines 95 to +122
@@ -117,6 +109,17 @@ protected final CompletableFuture<WorkflowModel> startExecution(
return future;
}

private CompletableFuture<WorkflowModel> processTaskList(WorkflowContext workflowContext) {
WorkflowModel inputModel =
workflowContext
.definition()
.inputFilter()
.map(f -> f.apply(workflowContext, null, input))
.orElse(input);
return TaskExecutorHelper.processTaskList(
workflowContext.definition().startTask(), workflowContext, Optional.empty(), inputModel);
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this really changing something or it is just a not needed refactor?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will remove this one in the next commit, I will add a validation at build method.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

WorkflowDefinition crashes when workflow has no tasks

3 participants