flowable/flowable-engine · error · FlowableException

Cannot propagate escalation '" + escalationName + "' with co

Error message

Cannot propagate escalation '" + escalationName + "' with code '" + escalationCode + "', because " + boundaryExecution + " is suspended

What it means

EscalationPropagation.executeEventHandler throws this when a thrown escalation is caught by a boundary escalation event whose execution is currently suspended. The engine refuses to deliver the escalation into a suspended scope, since resumed processing of a suspended execution is not allowed.

Source

Thrown at modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/helper/EscalationPropagation.java:217

            ExecutionEntityManager executionEntityManager = CommandContextUtil.getExecutionEntityManager();

            ExecutionEntity eventSubProcessExecution = executionEntityManager.createChildExecution(parentExecution);
            eventSubProcessExecution.setCurrentFlowElement(event.getSubProcess() != null ? event.getSubProcess() : event);
            CommandContextUtil.getAgenda().planContinueProcessOperation(eventSubProcessExecution);

        } else {
            ExecutionEntity boundaryExecution = null;
            List<? extends ExecutionEntity> childExecutions = parentExecution.getExecutions();
            for (ExecutionEntity childExecution : childExecutions) {
                if (childExecution != null
                        && childExecution.getActivityId() != null
                        && childExecution.getActivityId().equals(event.getId())) {
                    boundaryExecution = childExecution;
                }
            }
            
            if (boundaryExecution != null && boundaryExecution.isSuspended()) {
                throw new FlowableException(
                        "Cannot propagate escalation '" + escalationName + "' with code '" + escalationCode + "', because " + boundaryExecution
                                + " is suspended");
            }

            CommandContextUtil.getAgenda().planTriggerExecutionOperation(boundaryExecution);
        }
    }

    protected static Map<String, List<Event>> findCatchingEventsForProcess(String processDefinitionId, String escalationCode) {
        Map<String, List<Event>> eventMap = new HashMap<>();
        Process process = ProcessDefinitionUtil.getProcess(processDefinitionId);
        BpmnModel bpmnModel = ProcessDefinitionUtil.getBpmnModel(processDefinitionId);

        List<EventSubProcess> subProcesses = process.findFlowElementsOfType(EventSubProcess.class, true);
        for (EventSubProcess eventSubProcess : subProcesses) {
            for (FlowElement flowElement : eventSubProcess.getFlowElements()) {
                if (flowElement instanceof StartEvent startEvent) {
                    if (CollectionUtil.isNotEmpty(startEvent.getEventDefinitions()) && startEvent.getEventDefinitions()

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Resume the suspended execution/process instance (RuntimeService.activateProcessInstanceById / activate the relevant scope) before the escalation is thrown.
  2. If suspension is intentional, delay or suppress the escalation source (e.g. gate the throwEscalation path) until the scope is active.
  3. Check suspension state programmatically before triggering the escalation: query the execution and skip/queue if ExecutionEntity.isSuspended().
  4. Review which execution was suspended — suspending the parent instance suspends children; activate children explicitly if only the parent should resume.

Example fix

// before
runtimeService.throwEscalation(processInstanceId, "escalationCode"); // may hit suspended boundary execution

// after
if (!runtimeService.createProcessInstanceQuery().processInstanceId(id).singleResult().isSuspended()) {
    runtimeService.throwEscalation(id, "escalationCode");
} else {
    runtimeService.activateProcessInstanceById(id); // then throw
}
Defensive patterns

Strategy: validation

Validate before calling

ProcessInstance pi = runtimeService.createProcessInstanceQuery()
        .processInstanceId(piId).singleResult();
if (pi != null && pi.isSuspended()) {
    runtimeService.activateProcessInstanceById(piId); // before throwing escalation
}

Try / catch

try {
    runtimeService.throwEscalation(piId, code);
} catch (FlowableException e) {
    if (e.getMessage().contains("is suspended")) {
        runtimeService.activateProcessInstanceById(piId);
    }
}

Prevention

When it happens

Trigger: A subprocess (or call activity) throws an escalation; a boundary escalation event on that subprocess matches (by escalation code or default catch), but the childExecution holding the boundary event subscription is suspended (e.g. via RuntimeService.suspendProcessInstanceById or suspendJob for the scope).

Common situations: Suspending a process instance for maintenance/BAU hold while it (or its subprocess) later throws an escalation; suspension state persisted across restarts colliding with an incoming escalation from a call activity; resuming logic that activates the parent but not the child execution holding the boundary subscription.

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