Skip to content

StartAt activity in Fork produces wrong WorkflowStatus on 2nd iteration #7718

@NikosDevPhp

Description

@NikosDevPhp

Description

When a workflow containing a Fork → Join pattern with a timer-based branch (StartAt) and a blocking activity branch Restart is resumed on its second iteration (i.e., the loop restarts via a Continue blocking activity), the following exception is thrown if the timer deadline has already passed by the time the blocking activity is triggered:

System.InvalidOperationException: Sequence contains no elements

at System.Linq.ThrowHelper.ThrowNoElementsException()

at System.Linq.Enumerable.First[TSource](IEnumerable`1 source)

at Elsa.Handlers.WriteWorkflowSuspendExecutionLog.Handle(WorkflowSuspended notification, CancellationToken cancellationToken)

at Elsa.Services.Workflows.WorkflowRunner.RunWorkflowInternalAsync(...)

The root cause may be be a race condition in the Fork/Join resolution when one branch (the timer/Finish branch) has already elapsed and completes synchronously (returning Done instead of Suspend), while the other branch (the Restart) still expects to suspend the workflow. Elsa then attempts to record a WorkflowSuspended execution log entry but finds no blocking activities, causing the First() call to throw.

Critically, this exception does not prevent the post-Join logic (the SendMessageActivity) from executing, meaning external state is mutated even though the workflow is left in an inconsistent (Running) state.


Attachments

Image Image

Steps to Reproduce

  1. Create a workflow with the following structure (JSON attached):

    • A Continue blocking activity as the entry point of the loop.
    • A Fork with two branches:
      • Branch A: A Restart blocking activity — always suspends.
      • Branch Finish: A StartAt timer set to RequestDeadlineDate + 30 seconds, followed by a SetVariable (CompletedWorkflowTimePassed = True).
    • A Join (mode: WaitAny) after both branches.
    • Post-Join: an If checking CompletedWorkflowTimePassed, leading to a SendMessageActivity (CheckpointTypeName: Completed) and then Finish.
    • If CompletedWorkflowTimePassed is False, loop back to the Continue blocking activity (which first resets RequestDeadlineDate to now).
  2. Start the workflow. It suspends at the Continue blocking activity.

  3. Trigger the Continue action. The workflow proceeds into the Fork. Both branches begin — Branch A suspends on Restart, Branch Finish schedules the timer. Workflow suspends.

  4. Trigger the Restart action before the deadline + 30 seconds. The Join resolves, CompletedWorkflowTimePassed is False, the loop restarts, `

RequestDeadlineDateis reset to now, and the workflow re-suspends atContinue`. Works correctly.

  1. Wait more than 30 seconds.

  2. Trigger Continue again to start the second iteration. The Fork is entered again. Branch A suspends. Branch Finish triggers a new StartAt which resolves immediately with Outcome Done and not Suspend.

  3. Join runs and clears blocking activities, WorkflowStatus is left to suspended and WorkflowSuspended is emitted which tries to access the WorkflowInstance.BlockingActivities.First() and throws in WriteWorkflowSuspendExecutionLog notification handler.

  4. The workflow is left in Running state, but the State Change in External System fires anyway.

Reproduction Rate: Every time, when the blocking activity is triggered after the timer branch has already elapsed on any iteration after the first.

Workflow JSON:

{
  "$id": "1",
  "definitionId": "bacb11143e5f435fac9b0eef06307a5b",
  "versionId": "36b880ec22c64b158bd726dcce6338b7",
  "name": "Test",
  "displayName": "Test",
  "description": "Test",
  "version": 16,
  "variables": {
    "$id": "2",
    "data": {}
  },
  "customAttributes": {
    "$id": "3",
    "data": {}
  },
  "isSingleton": false,
  "persistenceBehavior": "WorkflowBurst",
  "deleteCompletedInstances": false,
  "isPublished": true,
  "isLatest": true,
  "tag": "Test",
  "createdAt": "2026-06-12T10:16:49.8871555Z",
  "activities": [
    {
      "$id": "4",
      "activityId": "11dc4c42-ac98-4698-8cb2-1619529b3ae9",
      "type": "Fork",
      "displayName": "Fork",
      "persistWorkflow": false,
      "loadWorkflowContext": false,
      "saveWorkflowContext": false,
      "properties": [
        {
          "$id": "5",
          "name": "Branches",
          "expressions": {
            "$id": "6",
            "Json": "[\"A\",\"Finish\"]"
          }
        }
      ],
      "propertyStorageProviders": {
        "$id": "7"
      }
    },
    {
      "$id": "8",
      "activityId": "d5409f16-e11b-44dc-844c-25f5b0a70d6c",
      "type": "AwaitActionActivity",
      "displayName": "Restart Blocking Activity",
      "persistWorkflow": false,
      "loadWorkflowContext": false,
      "saveWorkflowContext": false,
      "properties": [
        {
          "$id": "9",
          "name": "ActionName",
          "expressions": {
            "$id": "10",
            "Literal": "Restart"
          }
        },
        {
          "$id": "11",
          "name": "ActionLabel",
          "expressions": {
            "$id": "12",
            "Literal": "Restart"
          }
        },
        {
          "$id": "13",
          "name": "ActionInputDefaultValue",
          "expressions": {
            "$id": "14"
          }
        },
        {
          "$id": "15",
          "name": "ActionDescription",
          "expressions": {
            "$id": "16"
          }
        },
        {
          "$id": "17",
          "name": "ActionClass",
          "expressions": {
            "$id": "18"
          }
        },
        {
          "$id": "19",
          "name": "RedirectToList",
          "expressions": {
            "$id": "20"
          }
        },
        {
          "$id": "21",
          "name": "SuccessMessage",
          "expressions": {
            "$id": "22"
          }
        },
        {
          "$id": "23",
          "name": "AllowedRole",
          "expressions": {
            "$id": "24"
          }
        },
        {
          "$id": "25",
          "name": "ShowInput",
          "expressions": {
            "$id": "26"
          }
        },
        {
          "$id": "27",
          "name": "CaseId",
          "expressions": {
            "$id": "28"
          }
        },
        {
          "$id": "29",
          "name": "HandleActivityError",
          "expressions": {
            "$id": "30"
          }
        }
      ],
      "propertyStorageProviders": {
        "$id": "31"
      }
    },
    {
      "$id": "32",
      "activityId": "ed831d4d-1415-48dd-accc-d6c5c9e9c761",
      "type": "Join",
      "displayName": "Join",
      "persistWorkflow": false,
      "loadWorkflowContext": false,
      "saveWorkflowContext": false,
      "properties": [
        {
          "$id": "33",
          "name": "EagerJoin",
          "expressions": {
            "$id": "34"
          }
        },
        {
          "$id": "35",
          "name": "Mode",
          "expressions": {
            "$id": "36",
            "Literal": "WaitAny"
          }
        }
      ],
      "propertyStorageProviders": {
        "$id": "37"
      }
    },
    {
      "$id": "38",
      "activityId": "e86faa84-b707-43a3-9876-4593e0b351cb",
      "type": "StartAt",
      "displayName": "Start at DeadlineDate + 30 Seconds",
      "persistWorkflow": false,
      "loadWorkflowContext": false,
      "saveWorkflowContext": false,
      "properties": [
        {
          "$id": "39",
          "name": "Instant",
          "syntax": "JavaScript",
          "expressions": {
            "$id": "40",
            "JavaScript": "var deadline = new Date(getVariable(\"RequestDeadlineDate\"));\r\nvar deadlineInstant = instantFromUtc(\r\n    deadline.getUTCFullYear(),\r\n    deadline.getUTCMonth() + 1,\r\n    deadline.getUTCDate(),\r\n    deadline.getUTCHours(),\r\n    deadline.getUTCMinutes(),\r\n    deadline.getUTCSeconds()\r\n);\r\nreturn deadlineInstant.Plus(Duration.FromSeconds(30));"
          }
        }
      ],
      "propertyStorageProviders": {
        "$id": "41"
      }
    },
    {
      "$id": "42",
      "activityId": "24093c98-9826-4984-9d47-5c39ec5badd4",
      "type": "SetVariable",
      "displayName": "Set CompletedWorkflowTimePassed True",
      "persistWorkflow": false,
      "loadWorkflowContext": false,
      "saveWorkflowContext": false,
      "properties": [
        {
          "$id": "43",
          "name": "VariableName",
          "expressions": {
            "$id": "44",
            "Literal": "CompletedWorkflowTimePassed"
          }
        },
        {
          "$id": "45",
          "name": "Value",
          "expressions": {
            "$id": "46",
            "Literal": "True"
          }
        }
      ],
      "propertyStorageProviders": {
        "$id": "47"
      }
    },
    {
      "$id": "48",
      "activityId": "e907f01f-83af-42a5-9aca-0eea9c79abcc",
      "type": "If",
      "displayName": "CompletedWorkflowTimePassed True?",
      "persistWorkflow": false,
      "loadWorkflowContext": false,
      "saveWorkflowContext": false,
      "properties": [
        {
          "$id": "49",
          "name": "Condition",
          "syntax": "JavaScript",
          "expressions": {
            "$id": "50",
            "JavaScript": "return getVariable(\"CompletedWorkflowTimePassed\");"
          }
        }
      ],
      "propertyStorageProviders": {
        "$id": "51"
      }
    },
    {
      "$id": "52",
      "activityId": "7ea078c1-ead7-491e-b5de-4a39e021d552",
      "type": "Finish",
      "displayName": "Finish",
      "persistWorkflow": false,
      "loadWorkflowContext": false,
      "saveWorkflowContext": false,
      "properties": [
        {
          "$id": "53",
          "name": "ActivityOutput",
          "expressions": {
            "$id": "54"
          }
        },
        {
          "$id": "55",
          "name": "OutcomeNames",
          "expressions": {
            "$id": "56"
          }
        }
      ],
      "propertyStorageProviders": {
        "$id": "57"
      }
    },
    {
      "$id": "58",
      "activityId": "3d7c1822-9ec8-4456-8b5d-25cfa6cd1653",
      "type": "SetVariable",
      "displayName": "Set DeadlineDate To Now",
      "persistWorkflow": false,
      "loadWorkflowContext": false,
      "saveWorkflowContext": false,
      "properties": [
        {
          "$id": "59",
          "name": "VariableName",
          "expressions": {
            "$id": "60",
            "Literal": "RequestDeadlineDate"
          }
        },
        {
          "$id": "61",
          "name": "Value",
          "syntax": "JavaScript",
          "expressions": {
            "$id": "62",
            "JavaScript": "return currentInstant();"
          }
        }
      ],
      "propertyStorageProviders": {
        "$id": "63"
      }
    },
    {
      "$id": "64",
      "activityId": "4ee82f99-34d7-4fc5-9293-ebedae261e76",
      "type": "SendMessageActivity",
      "displayName": "State Change in External System",
      "persistWorkflow": false,
      "loadWorkflowContext": false,
      "saveWorkflowContext": false,
      "properties": [
        {
          "$id": "65",
          "name": "Message",
          "syntax": "JavaScript",
          "expressions": {
            "$id": "66",
            "JavaScript": "return {\r\n    CheckpointTypeName: 'Completed'\r\n};"
          }
        },
        {
          "$id": "67",
          "name": "RunAsSystemUser",
          "expressions": {
            "$id": "68"
          }
        },
        {
          "$id": "69",
          "name": "CaseId",
          "expressions": {
            "$id": "70"
          }
        },
        {
          "$id": "71",
          "name": "HandleActivityError",
          "expressions": {
            "$id": "72"
          }
        }
      ],
      "propertyStorageProviders": {
        "$id": "73"
      }
    },
    {
      "$id": "74",
      "activityId": "8824e93d-0a43-41fa-b915-b45383fe18e8",
      "type": "AwaitActionActivity",
      "displayName": "Continue Blocking Activity",
      "persistWorkflow": false,
      "loadWorkflowContext": false,
      "saveWorkflowContext": false,
      "properties": [
        {
          "$id": "75",
          "name": "ActionName",
          "expressions": {
            "$id": "76",
            "Literal": "Continue"
          }
        },
        {
          "$id": "77",
          "name": "ActionLabel",
          "expressions": {
            "$id": "78",
            "Literal": "Continue"
          }
        },
        {
          "$id": "79",
          "name": "ActionInputDefaultValue",
          "expressions": {
            "$id": "80"
          }
        },
        {
          "$id": "81",
          "name": "ActionDescription",
          "expressions": {
            "$id": "82"
          }
        },
        {
          "$id": "83",
          "name": "ActionClass",
          "expressions": {
            "$id": "84"
          }
        },
        {
          "$id": "85",
          "name": "RedirectToList",
          "expressions": {
            "$id": "86"
          }
        },
        {
          "$id": "87",
          "name": "SuccessMessage",
          "expressions": {
            "$id": "88"
          }
        },
        {
          "$id": "89",
          "name": "AllowedRole",
          "expressions": {
            "$id": "90"
          }
        },
        {
          "$id": "91",
          "name": "ShowInput",
          "expressions": {
            "$id": "92"
          }
        },
        {
          "$id": "93",
          "name": "CaseId",
          "expressions": {
            "$id": "94"
          }
        },
        {
          "$id": "95",
          "name": "HandleActivityError",
          "expressions": {
            "$id": "96"
          }
        }
      ],
      "propertyStorageProviders": {
        "$id": "97"
      }
    }
  ],
  "connections": [
    {
      "$id": "98",
      "sourceActivityId": "11dc4c42-ac98-4698-8cb2-1619529b3ae9",
      "targetActivityId": "d5409f16-e11b-44dc-844c-25f5b0a70d6c",
      "outcome": "A"
    },
    {
      "$id": "99",
      "sourceActivityId": "e86faa84-b707-43a3-9876-4593e0b351cb",
      "targetActivityId": "24093c98-9826-4984-9d47-5c39ec5badd4",
      "outcome": "Done"
    },
    {
      "$id": "100",
      "sourceActivityId": "ed831d4d-1415-48dd-accc-d6c5c9e9c761",
      "targetActivityId": "e907f01f-83af-42a5-9aca-0eea9c79abcc",
      "outcome": "Done"
    },
    {
      "$id": "101",
      "sourceActivityId": "24093c98-9826-4984-9d47-5c39ec5badd4",
      "targetActivityId": "ed831d4d-1415-48dd-accc-d6c5c9e9c761",
      "outcome": "Done"
    },
    {
      "$id": "102",
      "sourceActivityId": "e907f01f-83af-42a5-9aca-0eea9c79abcc",
      "targetActivityId": "4ee82f99-34d7-4fc5-9293-ebedae261e76",
      "outcome": "True"
    },
    {
      "$id": "103",
      "sourceActivityId": "4ee82f99-34d7-4fc5-9293-ebedae261e76",
      "targetActivityId": "7ea078c1-ead7-491e-b5de-4a39e021d552",
      "outcome": "Done"
    },
    {
      "$id": "104",
      "sourceActivityId": "4ee82f99-34d7-4fc5-9293-ebedae261e76",
      "targetActivityId": "7ea078c1-ead7-491e-b5de-4a39e021d552",
      "outcome": "Failed"
    },
    {
      "$id": "105",
      "sourceActivityId": "3d7c1822-9ec8-4456-8b5d-25cfa6cd1653",
      "targetActivityId": "8824e93d-0a43-41fa-b915-b45383fe18e8",
      "outcome": "Done"
    },
    {
      "$id": "106",
      "sourceActivityId": "8824e93d-0a43-41fa-b915-b45383fe18e8",
      "targetActivityId": "11dc4c42-ac98-4698-8cb2-1619529b3ae9",
      "outcome": "Done"
    },
    {
      "$id": "107",
      "sourceActivityId": "e907f01f-83af-42a5-9aca-0eea9c79abcc",
      "targetActivityId": "8824e93d-0a43-41fa-b915-b45383fe18e8",
      "outcome": "False"
    },
    {
      "$id": "108",
      "sourceActivityId": "11dc4c42-ac98-4698-8cb2-1619529b3ae9",
      "targetActivityId": "ed831d4d-1415-48dd-accc-d6c5c9e9c761",
      "outcome": "B"
    },
    {
      "$id": "109",
      "sourceActivityId": "d5409f16-e11b-44dc-844c-25f5b0a70d6c",
      "targetActivityId": "ed831d4d-1415-48dd-accc-d6c5c9e9c761",
      "outcome": "Done"
    },
    {
      "$id": "110",
      "sourceActivityId": "11dc4c42-ac98-4698-8cb2-1619529b3ae9",
      "targetActivityId": "e86faa84-b707-43a3-9876-4593e0b351cb",
      "outcome": "Finish"
    }
  ],
  "id": "36b880ec22c64b158bd726dcce6338b7"
}

Expected Behavior

When the Restart on Branch A is triggered after the timer on Branch Finish has already elapsed, the Join (WaitAny) should resolve cleanly. The workflow should either:

  • Recognize that the timer branch already completed and handle the WorkflowSuspended notification gracefully (since the remaining blocking activity from Branch A was just consumed), or
  • Correctly determine the new suspension state based on any remaining blocking activities before firing the WorkflowSuspended notification.

The SendMessageActivity should not execute if the workflow cannot complete the suspension bookkeeping cleanly.


Actual Behavior

WriteWorkflowSuspendExecutionLog.Handle calls .First() on what turns out to be an empty collection of blocking activities. This happens because:

  1. On the second (or later) iteration, when the Fork is re-entered, the timer branch (StartAtSetVariable) has already passed its instant and resolves immediately (no Suspend, goes straight to Done).
  2. The Join (WaitAny) receives Done from the timer branch and resolves immediately as well.
  3. The post-Join If evaluates CompletedWorkflowTimePassed = True and proceeds to SendMessageActivityFinish.
  4. Meanwhile, Elsa also processes Branch A's Restart suspension and attempts to emit a WorkflowSuspended notification — but there are now zero blocking activities registered, causing the First() exception.
  5. The exception is swallowed at the runner level, but the State Change in External System has already executed, corrupting external state.
  6. The workflow instance is left in Running state indefinitely.

Screenshots

N/A — error is server-side. Log output included below.


Environment

  • Elsa Package Version: 2.13
  • Operating System: Windows
  • Browser and Version: N/A (server-side issue)
  • Message Bus: Rebus

Log Output

System.InvalidOperationException: Sequence contains no elements

at System.Linq.ThrowHelper.ThrowNoElementsException()

at System.Linq.Enumerable.First[TSource](IEnumerable1 source)    at Elsa.Handlers.WriteWorkflowSuspendExecutionLog.Handle(WorkflowSuspended notification, CancellationToken cancellationToken)    at MediatR.Wrappers.NotificationHandlerWrapperImpl1.<>c__DisplayClass0_0.<Handle>b__1(...)

at MediatR.NotificationPublishers.ForeachAwaitPublisher.Publish(...)

at Elsa.Services.Workflows.WorkflowRunner.RunWorkflowInternalAsync(...)

at Elsa.Services.Workflows.WorkflowRunner.RunWorkflowAsync(...)

at Elsa.Services.Workflows.WorkflowResumer.ResumeWorkflowAsync(...)

at Elsa.Services.Workflows.WorkflowInstanceExecutor.ExecuteAsync(...)

at Elsa.Decorators.LockingWorkflowInstanceExecutor.ExecuteAsync(...)

at Elsa.Services.Dispatch.Consumers.ExecuteWorkflowInstanceRequestConsumer.Handle(ExecuteWorkflowInstanceRequest message)

at Rebus.Pipeline.Receive.HandlerInvoker`1.Invoke()

[... Rebus pipeline frames omitted for brevity ...]

Additional Context

  1. The issue seems to go away if I set a Timer activity on the Finish branch so that it always suspends.
  2. I suspect this also affects Elsa 3.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No fields configured for Bug.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions