Engine error when compensation event handler fails


In my process I have some compensation handlers in case BPMN transaction will be cancelled. Now assume, that last action (assign SIM in HSS) fails and both compensation activities should be called. Unassign devices in DM will be called first, but it fails too (throw BMPN exception, which should be catched by exception boundary event). So, in my expectation, the process should be finished in state ‘Rollback failed’, but instead I get ProcessEngineException. And there is some strange stackstrace with ArrayIndexOutOfBoundsException

But if last compensation handler will fail (invalidate subscriptions in my case), engine will behave as expected

Part of stacktrace:
java.lang.ArrayIndexOutOfBoundsException: -1
at java.util.ArrayList.elementData(ArrayList.java:422) ~[?:1.8.0_181]
at java.util.ArrayList.get(ArrayList.java:435) ~[?:1.8.0_181]
at org.camunda.bpm.engine.impl.pvm.runtime.PvmExecutionImpl.createActivityExecutionMapping(PvmExecutionImpl.java:1564) ~[camunda-engine-7.12.0.jar:7.12.0]
at org.camunda.bpm.engine.impl.pvm.runtime.PvmExecutionImpl.createActivityExecutionMapping(PvmExecutionImpl.java:1480) ~[camunda-engine-7.12.0.jar:7.12.0]
at org.camunda.bpm.engine.impl.pvm.runtime.PvmExecutionImpl.createActivityExecutionMapping(PvmExecutionImpl.java:1493) ~[camunda-engine-7.12.0.jar:7.12.0]
at org.camunda.bpm.engine.impl.pvm.runtime.LegacyBehavior.isCompensationThrowing(LegacyBehavior.java:624) ~[camunda-engine-7.12.0.jar:7.12.0]
at org.camunda.bpm.engine.impl.pvm.runtime.operation.PvmAtomicOperationActivityInstanceEnd.eventNotificationsStarted(PvmAtomicOperationActivityInstanceEnd.java:50) ~[camunda-engine-7.12.0.jar:7.12.0]
at org.camunda.bpm.engine.impl.pvm.runtime.operation.PvmAtomicOperationDeleteCascadeFireActivityEnd.eventNotificationsStarted(PvmAtomicOperationDeleteCascadeFireActivityEnd.java:36) ~[camunda-engine-7.12.0.jar:7.12.0]
at org.camunda.bpm.engine.impl.pvm.runtime.operation.PvmAtomicOperationDeleteCascadeFireActivityEnd.eventNotificationsStarted(PvmAtomicOperationDeleteCascadeFireActivityEnd.java:31) ~[camunda-engine-7.12.0.jar:7.12.0]
at org.camunda.bpm.engine.impl.core.operation.AbstractEventAtomicOperation.execute(AbstractEventAtomicOperation.java:43) ~[camunda-engine-7.12.0.jar:7.12.0]
at org.camunda.bpm.engine.impl.interceptor.AtomicOperationInvocation.execute(AtomicOperationInvocation.java:99) ~[camunda-engine-7.12.0.jar:7.12.0]
at org.camunda.bpm.engine.impl.interceptor.CommandInvocationContext.invokeNext(CommandInvocationContext.java:131) ~[camunda-engine-7.12.0.jar:7.12.0]
at org.camunda.bpm.engine.impl.interceptor.CommandInvocationContext.performNext(CommandInvocationContext.java:111) ~[camunda-engine-7.12.0.jar:7.12.0]
at org.camunda.bpm.engine.impl.interceptor.CommandInvocationContext.performOperation(CommandInvocationContext.java:86) ~[camunda-engine-7.12.0.jar:7.12.0]
at org.camunda.bpm.engine.impl.persistence.entity.ExecutionEntity.performOperation(ExecutionEntity.java:646) ~[camunda-engine-7.12.0.jar:7.12.0]
at org.camunda.bpm.engine.impl.persistence.entity.ExecutionEntity.performOperation(ExecutionEntity.java:620) ~[camunda-engine-7.12.0.jar:7.12.0]
at org.camunda.bpm.engine.impl.pvm.runtime.operation.PvmAtomicOperationDeleteCascade.execute(PvmAtomicOperationDeleteCascade.java:64) ~[camunda-engine-7.12.0.jar:7.12.0]
at org.camunda.bpm.engine.impl.pvm.runtime.operation.PvmAtomicOperationDeleteCascade.execute(PvmAtomicOperationDeleteCascade.java:25) ~[camunda-engine-7.12.0.jar:7.12.0]
at org.camunda.bpm.engine.impl.interceptor.AtomicOperationInvocation.execute(AtomicOperationInvocation.java:99) ~[camunda-engine-7.12.0.jar:7.12.0]
at org.camunda.bpm.engine.impl.interceptor.CommandInvocationContext.invokeNext(CommandInvocationContext.java:131) ~[camunda-engine-7.12.0.jar:7.12.0]
at org.camunda.bpm.engine.impl.interceptor.CommandInvocationContext.performNext(CommandInvocationContext.java:111) ~[camunda-engine-7.12.0.jar:7.12.0]
at org.camunda.bpm.engine.impl.interceptor.CommandInvocationContext.performOperation(CommandInvocationContext.java:86) ~[camunda-engine-7.12.0.jar:7.12.0]
at org.camunda.bpm.engine.impl.persistence.entity.ExecutionEntity.performOperation(ExecutionEntity.java:646) ~[camunda-engine-7.12.0.jar:7.12.0]
at org.camunda.bpm.engine.impl.persistence.entity.ExecutionEntity.performOperation(ExecutionEntity.java:620) ~[camunda-engine-7.12.0.jar:7.12.0]

Hi,
in my process design, I make the assumption/guideline that compensation handlers should not throw business exceptions, they can however throw technical exceptions…

I suspect you may be getting this exception because you are effectively leaving the business transaction scope, but by throwing a business exception from within the compensation handler, you are effectively reentering the business transaction scope…(like I said, I suspect this is the case)

So how do I know what to treat as a business versus technical exception? The best advice Ive come across is its not the type of error, its how the error is treated which determines the error mechanism to implement. If the error is tretsed by technical resources, create a technical error which manifests as an incident. If the error is managed by business resources, throw a bpmn error and model in the process.

If your compensation error truly is a business error, and the challenge is you need to propagate this business error outside of a business transaction, then you cold set a process variable flag to rollback failed and have a decisions gate after the transaction, or a conditional event subprocess…

As I inferred above, Im not completely sure of the BPMN semantics and the engine’s implementation of throwing a BPMN error from a compensation handler…

regards

Rob