flowable/flowable-engine · error · FlowableException

No outgoing sequence flow of element '${flowNode.getId()}' c

Error message

No outgoing sequence flow of element '${flowNode.getId()}' could be selected for continuing the process for ${execution}

What it means

After leaving a flow node, TakeOutgoingSequenceFlowsOperation must select outgoing sequence flows. If conditions on existing outgoing flows prevented all of them from being taken, the execution has nowhere to go and the engine throws this FlowableException instead of silently stalling.

Source

Thrown at modules/flowable-engine/src/main/java/org/flowable/engine/impl/agenda/TakeOutgoingSequenceFlowsOperation.java:250

        if (outgoingSequenceFlows.size() == 0 && evaluateConditions) { // The elements that set this to false also have no support for default sequence flow
            if (defaultSequenceFlowId != null) {
                for (SequenceFlow sequenceFlow : flowNode.getOutgoingFlows()) {
                    if (defaultSequenceFlowId.equals(sequenceFlow.getId())) {
                        outgoingSequenceFlows.add(sequenceFlow);
                        break;
                    }
                }
            }
        }

        // No outgoing found. Ending the execution
        if (outgoingSequenceFlows.size() == 0) {
            if (flowNode.getOutgoingFlows() == null || flowNode.getOutgoingFlows().size() == 0) {
                LOGGER.debug("No outgoing sequence flow found for flow node '{}'.", flowNode.getId());
                agenda.planEndExecutionOperation(execution);

            } else {
                throw new FlowableException("No outgoing sequence flow of element '" + flowNode.getId() + "' could be selected for continuing the process for " + execution);
            }

        } else {

            // Leave, and reuse the incoming sequence flow, make executions for all the others (if applicable)
            ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(commandContext);
            ExecutionEntityManager executionEntityManager = processEngineConfiguration.getExecutionEntityManager();
            List<ExecutionEntity> outgoingExecutions = new ArrayList<>(flowNode.getOutgoingFlows().size());

            SequenceFlow sequenceFlow = outgoingSequenceFlows.get(0);

            // Reuse existing one
            execution.setCurrentFlowElement(sequenceFlow);
            execution.setActive(false);
            outgoingExecutions.add(execution);

            // Executions for all the other one
            if (outgoingSequenceFlows.size() > 1) {

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Add a default flow (default attribute) to the node/gateway to catch unmatched cases
  2. Audit condition expressions and the variable values at runtime (check execution variables)
  3. Make one condition a catch-all (e.g. true) if a branch must always be taken
  4. Fix the data/process so a branch condition is always satisfied

Example fix

// before
<exclusiveGateway id="decision" />
<sequenceFlow id="f1" sourceRef="decision" targetRef="big"><conditionExpression>${amount > 100}</conditionExpression></sequenceFlow>
<sequenceFlow id="f2" sourceRef="decision" targetRef="small"><conditionExpression>${amount > 500}</conditionExpression></sequenceFlow>
// after
<exclusiveGateway id="decision" default="f2" />
<sequenceFlow id="f1" sourceRef="decision" targetRef="big"><conditionExpression>${amount > 100}</conditionExpression></sequenceFlow>
<sequenceFlow id="f2" sourceRef="decision" targetRef="small" />
Defensive patterns

Strategy: validation

Validate before calling

// Ensure every exclusive gateway has a default flow before deploying
for (Gateway gw : bpmnModel.getMainProcess().findElementsOfProcess(Gateway.class)) {
    if (gw instanceof ExclusiveGateway && ((ExclusiveGateway) gw).getDefaultFlow() == null
        && gw.getOutgoingFlows().stream().allMatch(f -> f.getConditionExpression() != null)) {
        throw new IllegalStateException("Gateway " + gw.getId() + " lacks a default flow");
    }
}

Try / catch

try {
    // process advance
} catch (FlowableException e) {
    if (e.getMessage().startsWith("No outgoing sequence flow")) {
        log.error("Unmatched conditions at node {}; check variables: {}", extractNodeId(e), vars);
    }
    throw e;
}

Prevention

When it happens

Trigger: leaveFlowNode (from handleFlowNode or handleAdhocSubProcess): the node has outgoing flows defined, but every conditional flow evaluated false and there is no default flow, leaving zero selected flows.

Common situations: Exclusive gateways/tasks with conditions like ${amount > 100} that no branch matches; data-driven conditions not satisfied by actual variables; missing default flow.

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/ac3c14999a76005e. Report an issue: GitHub.