NullPointerException when executing Boundary Event after migration to Flowable 6.5

After upgrading from Flowable 6.4.2 to 6.5.0, existing processes that have an Event Subprocess with an Error End Event and Boundary Error Event have started throwing NullPointerExceptions when processing the Boundary Error Event.

Here’s a sample bpmn which exhibits the change in behavior between 6.4.2 and 6.5.0

<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:flowable="http://flowable.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.flowable.org/processdef">
  <process id="test_error_boundary" name="test error boundary" isExecutable="true">
    <startEvent id="startEvent1" flowable:formFieldValidation="true"></startEvent>
    <exclusiveGateway id="sid-DDC2CF19-5BBC-4A96-A065-BE514CF16CE4"></exclusiveGateway>
    <sequenceFlow id="sid-E9156686-9C87-441B-8FF1-948CCA6769AA" sourceRef="startEvent1" targetRef="sid-DDC2CF19-5BBC-4A96-A065-BE514CF16CE4"></sequenceFlow>
    <serviceTask id="sid-F3A95B3F-46E3-440D-81C2-3DADFE31D1F1" flowable:delegateExpression="${throwingDelegate}"></serviceTask>
    <sequenceFlow id="sid-2588468D-8D2E-4E7E-9606-0374A3F970FC" sourceRef="sid-DDC2CF19-5BBC-4A96-A065-BE514CF16CE4" targetRef="sid-F3A95B3F-46E3-440D-81C2-3DADFE31D1F1"></sequenceFlow>
    <endEvent id="sid-9C9DF9C1-13CD-4845-923B-D18614C7046A"></endEvent>
    <sequenceFlow id="sid-F594B58C-9C7D-476B-901B-BD5BC6AC3EE8" sourceRef="sid-F3A95B3F-46E3-440D-81C2-3DADFE31D1F1" targetRef="sid-9C9DF9C1-13CD-4845-923B-D18614C7046A"></sequenceFlow>
    <subProcess id="error_sub_process" name="Error Sub Process" triggeredByEvent="true">
      <startEvent id="sid-233FE319-A488-4A2C-9EAE-EB2F36904983" isInterrupting="true">
        <errorEventDefinition errorRef="restClientError"></errorEventDefinition>
      </startEvent>
      <exclusiveGateway id="sid-ED4F9F87-4F76-492F-8352-9E800782EC21"></exclusiveGateway>
      <endEvent id="sid-C1610AD2-A69A-4FE9-97AF-90F7C77DCA03">
        <errorEventDefinition errorRef="handledError"></errorEventDefinition>
      </endEvent>
      <sequenceFlow id="sid-482147B9-1DC2-42CC-A75E-AEF07E45EAB0" sourceRef="sid-233FE319-A488-4A2C-9EAE-EB2F36904983" targetRef="sid-ED4F9F87-4F76-492F-8352-9E800782EC21"></sequenceFlow>
      <sequenceFlow id="sid-9096CCD1-A495-4871-8D80-BCCB51398CE8" sourceRef="sid-ED4F9F87-4F76-492F-8352-9E800782EC21" targetRef="sid-C1610AD2-A69A-4FE9-97AF-90F7C77DCA03"></sequenceFlow>
    </subProcess>
    <sequenceFlow id="sid-448398F9-17DB-4795-81D2-FBB49B748E3D" sourceRef="sid-15467FC7-B823-474F-9E68-C35CAA5B3328" targetRef="sid-9C9DF9C1-13CD-4845-923B-D18614C7046A"></sequenceFlow>
    <boundaryEvent id="sid-15467FC7-B823-474F-9E68-C35CAA5B3328" attachedToRef="error_sub_process">
      <errorEventDefinition errorRef="handledError"></errorEventDefinition>
    </boundaryEvent>
  </process>
</definitions>

The java delegate is a simple delegate which throws a BPMNError:

public class ThrowingDelegate implements JavaDelegate {
  @Override
  public void execute(DelegateExecution execution) {
    throw new BpmnError("restClientError");
  }
}

In Flowable 6.4.2, the process completes successfully, however when the process is executed in 6.5.0 the following exception is thrown:

2020-03-18 21:14:37.728 ERROR 11 --- [qtp247944893-16] o.f.c.e.impl.interceptor.CommandContext  : Error while closing command context
java.lang.NullPointerException: null
	at org.flowable.engine.impl.agenda.AbstractOperation.getCurrentFlowElement(AbstractOperation.java:53)
	at org.flowable.engine.impl.agenda.TriggerExecutionOperation.run(TriggerExecutionOperation.java:52)
	at org.flowable.engine.impl.interceptor.CommandInvoker.executeOperation(CommandInvoker.java:88)
	at org.flowable.engine.impl.interceptor.CommandInvoker.executeOperations(CommandInvoker.java:72)
	at org.flowable.engine.impl.interceptor.CommandInvoker.execute(CommandInvoker.java:56)
	at org.flowable.engine.impl.interceptor.BpmnOverrideContextInterceptor.execute(BpmnOverrideContextInterceptor.java:25)
	at org.flowable.common.engine.impl.interceptor.TransactionContextInterceptor.execute(TransactionContextInterceptor.java:53)
	at org.flowable.common.engine.impl.interceptor.CommandContextInterceptor.execute(CommandContextInterceptor.java:72)
	at org.flowable.common.spring.SpringTransactionInterceptor.execute(SpringTransactionInterceptor.java:51)
	at org.flowable.common.engine.impl.interceptor.LogInterceptor.execute(LogInterceptor.java:30)
	at org.flowable.common.engine.impl.cfg.CommandExecutorImpl.execute(CommandExecutorImpl.java:56)
	at org.flowable.common.engine.impl.cfg.CommandExecutorImpl.execute(CommandExecutorImpl.java:51)
	at org.flowable.engine.impl.RuntimeServiceImpl.startProcessInstance(RuntimeServiceImpl.java:732)
	at org.flowable.engine.impl.runtime.ProcessInstanceBuilderImpl.start(ProcessInstanceBuilderImpl.java:205)
	at org.flowable.engine.impl.RuntimeServiceImpl.startProcessInstanceWithForm(RuntimeServiceImpl.java:171)
	at org.flowable.ui.task.service.runtime.FlowableProcessInstanceService.startNewProcessInstance(FlowableProcessInstanceService.java:132)
	at org.flowable.ui.task.service.runtime.FlowableProcessInstanceService$$FastClassBySpringCGLIB$$6558533.invoke(<generated>)
	at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:769)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:747)
	at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:366)
	at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:99)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:747)
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:689)
	at org.flowable.ui.task.service.runtime.FlowableProcessInstanceService$$EnhancerBySpringCGLIB$$6b32ef7f.startNewProcessInstance(<generated>)
	at org.flowable.ui.task.rest.runtime.ProcessInstancesResource.startNewProcessInstance(ProcessInstancesResource.java:33)

I’ve tracked it down to the following within ErrorPropagation#executeEventHander where the double starred condition is what fails causing boundaryExecution to be null

childExecution.getActivityId() = sid-C1610AD2-A69A-4FE9-97AF-90F7C77DCA03
event.getId() = sid-15467FC7-B823-474F-9E68-C35CAA5B3328

} 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;
  }
}

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

The question is, should something change in the BPMN or was a bug introduced in Flowable 6.5? Nothing other than the version of Flowable changed. I also tested the same scenario using end escalation events and boundary escalation events with the same results. Clearly I’m doing something wrong here, but I’m not sure what that is. Any suggestions would be greatly appreciated.

Thanks in advance.

Hey @lrx007,

Thanks for the detailed example and the analysis it helped us track down the problem.

In 6.4 this worked by accident. What you are trying to do is not a valid BPMN and should have never been possible to do it. From Section 13.4.4 Event Sub-Processes the definition for them is the following:

Event Sub-Processes allow to handle an Event within the context of a given Sub-Processes or Process. An Event Sub-Process always begins with a Start Event, followed by Sequence Flows. Event Sub-Processes are a special kind of Sub-Process: they create a scope and are instantiated like a Sub-Process, but they are not instantiated by normal control flow but only when the associated Start Event is triggered. Event Sub-Processes are self-contained and MUST not be connected to the rest of the Sequence Flows in the Sub-Processes; also they cannot have attached boundary Events. They run in the context of the Sub-Process, and thus have access to its context.

The important part from that definition is

also they cannot have attached boundary Events

We are going to add an extra validation check to validate this kind of errors.

All this said, the question is what exactly are you trying to model. Perhaps combining CMMN and BPMN together would be more beneficial for you. CMMN is more flexible and you can express concepts like you are trying to do in a more descriptive manner.

Cheers,
Filip

Thanks @filiphr, the documentation does makes sense and lucky us that it worked for so long. :grin:

This issue we’re trying to solve here is centered around error handling and returning to the start of the process instance upon resolution. What we have been doing is:

  1. Catch the error in the even sub process
  2. Send a notification and enter a User Task
  3. Return to the first step in the flow through the boundary event when user task is completed.

I think maybe simplest direction, for now, would be to wrap our current flow in a sub process with an attached boundary error event which branches to our error handling and then returns to the sub process. No event sub process in that flow as in the attached diagram.

Or, is there a way to return to the main flow sequence from an event sub process that I’m not seeing?

We’ll certainly look into to combining BPMN and CMMN to see if it makes more sense.

Yes that would be one option indeed. What you can also do is to have the event sub process within the sub process. It will then get activated on restClientError and then have a boundary on the sub process (not the event one) that would handle the handledError.

It is not possible to return to the main flow from an event sub process. According to the spec they are:

Event Sub-Processes are self-contained and MUST not be connected to the rest of the Sequence Flows in the Sub-Processes

We have additionally added validation for event sub processes with boundary events in this commit and have made sure that there is no NPE like you are seeing in case one uses a boundary event in this commit. Basically boundary events are completely ignored, there won’t be an NPE but the user task will also not be activated :slight_smile:.

Cheers,
Filip

@filiphr Thank you so much for the input and the quick responses. We’re not sure which way we’re going to handle things moving forward but we definitely understand our options and what not to do. Keep up the good work!