Skip to content

Event subprocesses

Stephan Pelikan edited this page Oct 11, 2026 · 2 revisions

An event subprocess sits inside a process and waits for its start event. No sequence flow leads into it, so nothing in the drawing says when it runs. It starts when a timer goes off, when a message arrives, or when an error is thrown inside the scope around it. What it then does to the rest of the workflow depends on one attribute of that start event, isInterrupting.

VanillaBP tells you two things about a workflow which come from this construct. They are on pages far apart. This page says what the construct is, so that both make sense.

  1. The two forms
    Interrupting and non-interrupting, and what each does to the rest of the workflow.
  2. The tasks inside it are ordinary tasks
    Which methods serve them, and which process they belong to.
  3. The non-interrupting form writes your aggregate twice
    Why VanillaBP warns about it, and what the warning names.
  4. The interrupting form ends the workflow as completed
    What your @WorkflowEnded method is told, and what VanillaBP does with the open tasks.

The two forms

The start event of an event subprocess is either interrupting or not. BPMN draws the difference as a solid or a dashed circle.

An interrupting start event removes the scope around it. The token which was waiting somewhere in that scope is gone, and so are the tasks which belonged to it. The event subprocess runs instead. At process level this ends the workflow.

A non-interrupting start event adds a token and leaves the rest of the workflow running. The classic case is a reminder. While an approval task waits, a timer fires every other day, and a second path sends the reminder. Several instances of it may run at the same time, one for each time the event arrived.

The tasks inside it are ordinary tasks

A task drawn inside an event subprocess belongs to the process the subprocess sits in. Every adapter reads it that way. It walks up from the element to the process which owns it. The task is then wired to a @WorkflowTask method of the workflow service for that process, exactly like a task on the main path. There is no annotation for "this runs in an event subprocess". The wiring validation asks for a method for it, as for any other task.

The workflow aggregate is the same one, too. A handler in the event subprocess loads and saves the aggregate of the workflow it belongs to. This is why the next section matters.

The non-interrupting form writes your aggregate twice

Two tokens mean two writers, and nothing in the drawing points at the second one. While your approval handler works on the aggregate, the reminder handler does too. The transaction which commits second writes back what it read when it started. Nothing reports the lost write. Two writers on one aggregate has the long answer, and the ways out.

VanillaBP looks for this case while it deploys. Both Camunda adapters read the models they deploy. They collect the elements which can put a second token into a running workflow and hand them to the core. A non-interrupting event subprocess is one of them: a subprocess triggered by an event, with a start event which does not interrupt. If the workflow aggregate of that process has no version attribute, the core writes one warning for the process and names the elements it found. It names the subprocess itself, not the tasks inside it, because the second token comes from the subprocess.

An adapter which cannot read its models reports nothing here, and VanillaBP guesses nothing from the silence. The Process-Engine-API adapter is such a case. So you get this warning on the Camunda engines, but not there.

The interrupting form ends the workflow as completed

Suppose an interrupting event subprocess at process level ends a workflow. This reads like a cancelation, but it is reported as COMPLETED. Both Camunda engines do that. On Camunda 7 an integration test checks it. That test also shows which element is reported with the end: the event subprocess, not an end event. So an id coming out of WorkflowEnd is not always the id of an end event. For Camunda 8 it is on the adapter's Deviations page.

This is also why VanillaBP does not look at the kind of end when it works out which tasks disappeared with it. An end which completes the instance can still remove an open task. This construct is one of the two cases where that happens. If VanillaBP checked the kind first, it would skip exactly the cases the check is there for. What your application is told, and when, is in when a workflow ends and in completing and canceling asynchronous tasks. (why)

Clone this wiki locally