flowable/flowable-engine · error · FlowableException

${sequenceFlowId} does not match a sequence flow for ${deleg

Error message

${sequenceFlowId} does not match a sequence flow for ${delegateExecution}

What it means

DelegateHelper.leaveDelegate() resolves the execution's currentFlowElement as a sequenceFlowId from the BPMN model of the process definition. If the looked-up FlowElement is not a SequenceFlow (or is missing), Flowable throws this error, because it can only take outgoing flows when positioned on a sequence flow.

Source

Thrown at modules/flowable-engine/src/main/java/org/flowable/engine/delegate/DelegateHelper.java:63

    /**
     * To be used in an {@link ActivityBehavior} or {@link JavaDelegate}: leaves according to the default BPMN 2.0 rules: all sequenceflow with a condition that evaluates to true are followed.
     */
    public static void leaveDelegate(DelegateExecution delegateExecution) {
        CommandContextUtil.getAgenda().planTakeOutgoingSequenceFlowsOperation((ExecutionEntity) delegateExecution, true);
    }

    /**
     * To be used in an {@link ActivityBehavior} or {@link JavaDelegate}: leaves the current activity via one specific sequenceflow.
     */
    public static void leaveDelegate(DelegateExecution delegateExecution, String sequenceFlowId) {
        String processDefinitionId = delegateExecution.getProcessDefinitionId();
        org.flowable.bpmn.model.Process process = ProcessDefinitionUtil.getProcess(processDefinitionId);
        FlowElement flowElement = process.getFlowElement(sequenceFlowId);
        if (flowElement instanceof SequenceFlow) {
            delegateExecution.setCurrentFlowElement(flowElement);
            CommandContextUtil.getAgenda().planTakeOutgoingSequenceFlowsOperation((ExecutionEntity) delegateExecution, false);
        } else {
            throw new FlowableException(sequenceFlowId + " does not match a sequence flow for " + delegateExecution);
        }
    }

    /**
     * Returns the {@link BpmnModel} matching the process definition bpmn model for the process definition of the passed {@link DelegateExecution}.
     */
    public static BpmnModel getBpmnModel(DelegateExecution execution) {
        if (execution == null) {
            throw new FlowableException("Null execution passed");
        }
        return ProcessDefinitionUtil.getBpmnModel(execution.getProcessDefinitionId());
    }

    /**
     * Returns the current {@link FlowElement} where the {@link DelegateExecution} is currently at.
     */
    public static FlowElement getFlowElement(DelegateExecution execution) {
        BpmnModel bpmnModel = getBpmnModel(execution);

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Only invoke leaveDelegate when the execution is on a SequenceFlow (e.g. an execution listener attached to the sequence flow element)
  2. Check the currentFlowElement id exists in the current process definition's BPMN model before calling
  3. For logic on a node, use the appropriate agenda operation (planTakeOutgoingSequenceFlowsOperation on the activity) or DelegateHelper methods meant for flow elements
  4. Redeploy consistent model versions so running instances match the deployed definitions

Example fix

// before
public void execute(DelegateExecution execution) {
    DelegateHelper.leaveDelegate(execution); // executed on a ServiceTask
}

// after: attach to the sequence flow, or guard
FlowElement el = execution.getCurrentFlowElement();
if (el instanceof SequenceFlow) {
    DelegateHelper.leaveDelegate(execution);
}
Defensive patterns

Strategy: type-guard

Validate before calling

FlowElement el = execution.getCurrentFlowElement();
boolean isSequenceFlow = el != null && el.getId() != null
    && ProcessDefinitionUtil.getProcessDefinition(execution.getProcessDefinitionId()) != null
    && DelegateHelper.getBpmnModel(execution).getMainProcess().getFlowElement(el.getId()) instanceof SequenceFlow;

Type guard

boolean canLeaveDelegate(DelegateExecution e) {
    FlowElement el = e.getCurrentFlowElement();
    return el instanceof SequenceFlow;
}

Try / catch

try {
    DelegateHelper.leaveDelegate(execution);
} catch (FlowableException e) {
    if (e.getMessage().contains("does not match a sequence flow")) {
        // wrong element type: route logic to the appropriate node/flow listener
    } else throw e;
}

Prevention

When it happens

Trigger: Calling DelegateHelper.leaveDelegate(delegateExecution) while the delegate execution's currentFlowElement id (sequenceFlowId) does not exist in the process definition, or points to a different element type (task, event, gateway).

Common situations: Executing leaveDelegate from a listener/execution-logic attached to a node rather than to a sequence flow; changing the BPMN XML (renaming/removing the flow id) while old deployed process instances still run with cached state; copying delegate code between process models.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11). Data as JSON: /api/errors/07dfa717ff6b0a3b. Report an issue: GitHub.