Sequence Flow
Sequence flows connect flow nodes — events, tasks, gateways, and sub-processes — and determine which token paths are taken at execution time.
Attributes
| Attribute | Required | Notes |
|---|---|---|
id | yes | Unique flow identifier |
name | no | Display label |
sourceRef | yes | ID of the source node |
targetRef | yes | ID of the target node |
Conditions
A <conditionExpression> child carries a FEEL expression evaluated when the token leaves a node with multiple outgoing flows. The flow is taken if the expression returns true.
<bpmn:sequenceFlow id="f1" sourceRef="svc1" targetRef="task-success">
<bpmn:conditionExpression xsi:type="bpmn:tFormalExpression">=price > 0</bpmn:conditionExpression>
</bpmn:sequenceFlow>
Condition expressions can sit on flows leaving:
- Exclusive (XOR) gateways — exactly one matching flow is taken; ties resolve in document order.
- Inclusive (OR) gateways — every matching flow is taken in parallel.
- Activities — every matching flow is taken in parallel (uncontrolled fork, BPMN 2.0 §13.2.1).
A flow leaving a node that has only one outgoing flow ignores any condition and is always taken.
Default flows
A node with multiple outgoing flows can declare one of them as the default via the default="<flowId>" attribute. The default flow fires when every other outgoing flow's condition evaluates to false. The engine recognises default on:
- exclusive (XOR) gateways
- inclusive (OR) gateways
- tasks (service / user / script / business-rule / send / receive / manual / generic)
- sub-processes (embedded and ad-hoc)
- call activities
A default flow must not carry a <conditionExpression> of its own — its only job is to be the fallback when nothing else matches.
<!-- XOR gateway with a default -->
<bpmn:exclusiveGateway id="gw1" default="f-fallback">
<bpmn:outgoing>f-vip</bpmn:outgoing>
<bpmn:outgoing>f-fallback</bpmn:outgoing>
</bpmn:exclusiveGateway>
<bpmn:sequenceFlow id="f-vip" sourceRef="gw1" targetRef="task-vip">
<bpmn:conditionExpression>=tier = "vip"</bpmn:conditionExpression>
</bpmn:sequenceFlow>
<bpmn:sequenceFlow id="f-fallback" sourceRef="gw1" targetRef="task-default" />
<!-- Service task with a default — same shape, no extra gateway -->
<bpmn:serviceTask id="route" default="f-default">
<bpmn:extensionElements>
<zeebe:taskDefinition type="route-task" />
</bpmn:extensionElements>
<bpmn:outgoing>f-route-vip</bpmn:outgoing>
<bpmn:outgoing>f-default</bpmn:outgoing>
</bpmn:serviceTask>
How outgoing flows are selected
When a token leaves a node with multiple outgoing flows, the engine evaluates them in two passes:
- Non-default flows. Each outgoing flow whose condition evaluates to
trueemits a token. A flow without a<conditionExpression>is treated as always-true. The default flow (if any) is skipped in this pass. - Default fallback. If pass 1 emitted zero tokens and the node declares a default flow, the default emits a token.
Behaviour at a glance:
| Source node kind | Multiple non-default flows match | No non-default flows match | No conditional flows; only an unconditional default |
|---|---|---|---|
| Exclusive gateway (XOR) | Take the first match in document order; rest dropped | Take the default if declared; otherwise raise GatewayNoMatchError | Take the default |
| Inclusive gateway (OR) | Take every match in parallel | Take the default if declared; otherwise raise GatewayNoMatchError | Take the default |
| Activity | Emit a token on every match (uncontrolled fork) | Emit on the default if declared; otherwise the token simply has no successor and the activity's branch terminates | Emit on the default |
| Parallel gateway (AND) | Always emits on every outgoing flow; conditions are ignored (warning at validation time) | n/a | n/a |
Three-way example
Consider a service task with three outgoing flows:
f-cond— condition=x > 0, targettask-positive.f-uncond— no condition, targettask-always.f-default— declared asdefault, targettask-fallback.
<bpmn:serviceTask id="route" default="f-default">
<bpmn:extensionElements>
<zeebe:taskDefinition type="route-task" />
</bpmn:extensionElements>
<bpmn:outgoing>f-cond</bpmn:outgoing>
<bpmn:outgoing>f-uncond</bpmn:outgoing>
<bpmn:outgoing>f-default</bpmn:outgoing>
</bpmn:serviceTask>
<bpmn:sequenceFlow id="f-cond" sourceRef="route" targetRef="task-positive">
<bpmn:conditionExpression xsi:type="bpmn:tFormalExpression">=x > 0</bpmn:conditionExpression>
</bpmn:sequenceFlow>
<bpmn:sequenceFlow id="f-uncond" sourceRef="route" targetRef="task-always" />
<bpmn:sequenceFlow id="f-default" sourceRef="route" targetRef="task-fallback" />
Outcomes per x:
x value | f-cond (x > 0) | f-uncond (no condition) | f-default (default) | Result |
|---|---|---|---|---|
5 | true → emits | always-true → emits | skipped | Two parallel tokens: task-positive and task-always |
-1 | false → skipped | always-true → emits | skipped | One token: task-always |
| (impossible) | false | (cannot be false) | would emit | f-default never fires here |
The default flow f-default in this model is dead code: as long as f-uncond has no condition, the non-default pass always emits at least one token, and the fallback pass never runs. The validator does not flag this — the same model with a different unconditional sibling (e.g. an event) is intentional in some patterns — but it is worth catching in review. To make the default reachable, give f-uncond a condition that can be false (e.g. =alwaysTrue and not panic), or remove it.
Gateway-only nuance: parallel and event-based
- Parallel gateways (AND) ignore conditions on outgoing flows entirely. Every outgoing flow activates. A
<conditionExpression>here is a modeling error and the validator emits a warning. - Event-based gateways route on whichever attached intermediate catch event fires first. Their outgoing flows must not carry conditions (validation error) and must each lead to an
intermediateCatchEvent.
Validation
| Check | Severity |
|---|---|
| Default flow ID isn't one of the source node's outgoing flows | Error |
Default flow carries a conditionExpression | Error |
| Outgoing flow on an exclusive gateway has no condition (and no default is set) | Warning |
| Outgoing flow on a parallel gateway carries a condition | Warning (the condition is ignored) |
| Outgoing flow on an event-based gateway carries a condition | Error |
| Outgoing flow on an event-based gateway leads to a non-catch-event target | Error |
| Start event has any incoming sequence flow | Error |
| End event has any outgoing sequence flow | Error |
| Intermediate catch event does not have exactly one outgoing sequence flow | Error |
Activity marked isForCompensation="true" has incoming flows | Error |
Examples
<!-- Unconditional flow -->
<bpmn:sequenceFlow id="f1" sourceRef="start" targetRef="task1" />
<!-- Conditional flow off a gateway -->
<bpmn:sequenceFlow id="f2" name="price > 0" sourceRef="gw1" targetRef="task2">
<bpmn:conditionExpression xsi:type="bpmn:tFormalExpression">=price > 0</bpmn:conditionExpression>
</bpmn:sequenceFlow>
<!-- Default flow off a gateway (no condition) -->
<bpmn:sequenceFlow id="f3" name="default" sourceRef="gw1" targetRef="task3" />
<!-- gw1 must declare default="f3" -->
<!-- Conditional + default off a service task -->
<bpmn:serviceTask id="route" default="route-fallback">
<bpmn:extensionElements><zeebe:taskDefinition type="route-task"/></bpmn:extensionElements>
<bpmn:outgoing>route-vip</bpmn:outgoing>
<bpmn:outgoing>route-fallback</bpmn:outgoing>
</bpmn:serviceTask>
<bpmn:sequenceFlow id="route-vip" sourceRef="route" targetRef="task-vip">
<bpmn:conditionExpression>=tier = "vip"</bpmn:conditionExpression>
</bpmn:sequenceFlow>
<bpmn:sequenceFlow id="route-fallback" sourceRef="route" targetRef="task-standard" />