NullPointerException when migrating TimerEntity with multiple incidents

Hi, there!

We have synchronous migration of our process instances with mapEqualActivities() plan.

MigrationPlan migrationPlan = processEngine.getRuntimeService()
             .createMigrationPlan(srcDefId, tgtDefId)
             .mapEqualActivities()
             .updateEventTriggers()
             .build();

and then

processEngine.getRuntimeService().newMigration(migrationPlan)
             .processInstanceIds(instancesToMigrate)
             .skipCustomListeners().skipIoMappings()
             .execute();

When the process contains incidents on the user task boundary timer then migration (.execute()) leads to the following exceptions (even when migrating to the same definition, uploaded again):

Caused by: java.lang.NullPointerException: null
	at org.camunda.bpm.engine.impl.migration.instance.MigratingTimerJobInstance.migrateJobHandlerConfiguration(MigratingTimerJobInstance.java:69)
	at org.camunda.bpm.engine.impl.migration.instance.MigratingJobInstance.migrateState(MigratingJobInstance.java:91)
	at org.camunda.bpm.engine.impl.migration.instance.MigratingActivityInstance.migrateDependentEntities(MigratingActivityInstance.java:182)
	at org.camunda.bpm.engine.impl.migration.instance.MigratingProcessElementInstanceVisitor.migrateProcessElementInstance(MigratingProcessElementInstanceVisitor.java:90)
	at org.camunda.bpm.engine.impl.migration.instance.MigratingProcessElementInstanceVisitor.visit(MigratingProcessElementInstanceVisitor.java:36)
	at org.camunda.bpm.engine.impl.migration.instance.MigratingProcessElementInstanceVisitor.visit(MigratingProcessElementInstanceVisitor.java:31)
	at org.camunda.bpm.engine.impl.tree.ReferenceWalker.walkUntil(ReferenceWalker.java:90)
	at org.camunda.bpm.engine.impl.tree.ReferenceWalker.walkUntil(ReferenceWalker.java:68)
	at org.camunda.bpm.engine.impl.migration.MigrateProcessInstanceCmd.migrateProcessInstance(MigrateProcessInstanceCmd.java:315)
	at org.camunda.bpm.engine.impl.migration.MigrateProcessInstanceCmd$3.run(MigrateProcessInstanceCmd.java:158)
	at org.camunda.bpm.engine.impl.context.ProcessApplicationContextUtil.doContextSwitch(ProcessApplicationContextUtil.java:189)
	at org.camunda.bpm.engine.impl.migration.MigrateProcessInstanceCmd.executeInContext(MigrateProcessInstanceCmd.java:167)
	at org.camunda.bpm.engine.impl.migration.MigrateProcessInstanceCmd.migrateProcessInstance(MigrateProcessInstanceCmd.java:154)
	at org.camunda.bpm.engine.impl.migration.MigrateProcessInstanceCmd$1.call(MigrateProcessInstanceCmd.java:114)
	at org.camunda.bpm.engine.impl.migration.MigrateProcessInstanceCmd$1.call(MigrateProcessInstanceCmd.java:109)
	at org.camunda.bpm.engine.impl.interceptor.CommandContext.runWithoutAuthorization(CommandContext.java:477)
	at org.camunda.bpm.engine.impl.migration.MigrateProcessInstanceCmd.execute(MigrateProcessInstanceCmd.java:109)
	at org.camunda.bpm.engine.impl.migration.MigrateProcessInstanceCmd.execute(MigrateProcessInstanceCmd.java:75)
	at org.camunda.bpm.engine.impl.interceptor.CommandExecutorImpl.execute(CommandExecutorImpl.java:27)
	at org.camunda.bpm.engine.impl.interceptor.CommandContextInterceptor.execute(CommandContextInterceptor.java:106)
	at org.camunda.bpm.engine.spring.SpringTransactionInterceptor$1.doInTransaction(SpringTransactionInterceptor.java:45)
	at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:140)
	at org.camunda.bpm.engine.spring.SpringTransactionInterceptor.execute(SpringTransactionInterceptor.java:43)
	at org.camunda.bpm.engine.impl.interceptor.ProcessApplicationContextInterceptor.execute(ProcessApplicationContextInterceptor.java:69)
	at org.camunda.bpm.engine.impl.interceptor.LogInterceptor.execute(LogInterceptor.java:32)
	at org.camunda.bpm.engine.impl.migration.MigrationPlanExecutionBuilderImpl.execute(MigrationPlanExecutionBuilderImpl.java:99)
	at org.camunda.bpm.engine.impl.migration.MigrationPlanExecutionBuilderImpl.execute(MigrationPlanExecutionBuilderImpl.java:95)

We are using camunda-engine-spring 7.10.0 within the Spring Boot application.

While debugging the source mentioned in the stack trace I’ve figured out that org/camunda/bpm/engine/impl/migration/instance/parser/ActivityInstanceJobHandler#handle for the particular UserTask receives 61 elements (TimerEntity) among which 1 has jobHandlerConfiguration=BoundaryEvent_0awided and other 60 have jobHandlerConfiguration=BoundaryEvent_0awided$followUpJobCreated.
So the processing of the first of them leads to removal (targetTimerDeclarationsInEventScope.remove) of BoundaryEvent_0awided from the targetTimerDeclarationsInEventScope and following ones receive null in targetTimerDeclaration.

Examining the latest code I see that additional check for timeout listeners was added, but it seems not fixing the current problem.

Summing up, maybe in general it is not a right thing to migrate repeatable timer incidents. I’m looking for a way to automate the thing - migrate it to the new definition or remove. Am I missing some configuration option or an execution parameter?

PS. Sample elemets:

58 = {TimerEntity@17351} "TimerEntity[repeat=0 0/30 * * * ?, id=9eb8e7ab-2a45-11eb-81a5-0242ac10ee0c, revision=8, duedate=Thu Nov 19 11:30:00 UTC 2020, lockOwner=null, lockExpirationTime=null, executionId=105261a6-22a3-11eb-ba50-0242ac10ee0e, processInstanceId=1050dac2-22a3-11eb-ba50-0242ac10ee0e, isExclusive=true, retries=0, jobHandlerType=timer-transition, jobHandlerConfiguration=BoundaryEvent_0awided$followUpJobCreated, exceptionByteArray=null, exceptionByteArrayId=cfe03ac6-2a49-11eb-81a5-0242ac10ee0c, exceptionMessage=Text '' could not be parsed at index 0, deploymentId=7bc2a3f8-233e-11eb-81a5-0242ac10ee0c]"
59 = {TimerEntity@17352} "TimerEntity[repeat=0 0/30 * * * ?, id=86e14a4a-2a6f-11eb-81a5-0242ac10ee0c, revision=8, duedate=Thu Nov 19 16:30:00 UTC 2020, lockOwner=null, lockExpirationTime=null, executionId=105261a6-22a3-11eb-ba50-0242ac10ee0e, processInstanceId=1050dac2-22a3-11eb-ba50-0242ac10ee0e, isExclusive=true, retries=0, jobHandlerType=timer-transition, jobHandlerConfiguration=BoundaryEvent_0awided$followUpJobCreated, exceptionByteArray=null, exceptionByteArrayId=0b71d8f8-2e4a-11eb-a302-a85e45305b30, exceptionMessage=Text '' could not be parsed at index 0, deploymentId=7bc2a3f8-233e-11eb-81a5-0242ac10ee0c]"
60 = {TimerEntity@17353} "TimerEntity[repeat=0 0/30 * * * ?, id=0b2c4452-2e4a-11eb-a302-a85e45305b30, revision=1, duedate=Tue Nov 24 12:00:00 UTC 2020, lockOwner=null, lockExpirationTime=null, executionId=105261a6-22a3-11eb-ba50-0242ac10ee0e, processInstanceId=1050dac2-22a3-11eb-ba50-0242ac10ee0e, isExclusive=true, retries=3, jobHandlerType=timer-transition, jobHandlerConfiguration=BoundaryEvent_0awided, exceptionByteArray=null, exceptionByteArrayId=null, exceptionMessage=null, deploymentId=7bc2a3f8-233e-11eb-81a5-0242ac10ee0c]"