{"record":{"id":"59d78c04783e5fa9","repo":"flowable/flowable-engine","slug":"multiple-timer-jobs-found-for-plan-item-instance","errorCode":null,"errorMessage":"Multiple timer jobs found for plan item instance ","messagePattern":"Multiple timer jobs found for plan item instance ","errorType":"exception","errorClass":"FlowableException","httpStatus":null,"severity":"error","filePath":"modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/cmd/RescheduleTimerJobCmd.java","lineNumber":68,"sourceCode":"    @Override\n    public Job execute(CommandContext commandContext) {\n        String timerJobId = null;\n        PlanItemInstance planItemInstance = null;\n        JobService jobService = CommandContextUtil.getJobService(commandContext);\n        \n        if (eventListenerInstanceId != null) {\n            planItemInstance = CommandContextUtil.getPlanItemInstanceEntityManager(commandContext).findById(eventListenerInstanceId);\n            if (planItemInstance == null) {\n                throw new FlowableObjectNotFoundException(\"No plan item instance found for id \" + eventListenerInstanceId);\n            }\n            \n            List<Job> timerJobs = jobService.createTimerJobQuery().planItemInstanceId(eventListenerInstanceId).list();\n            if (timerJobs == null || timerJobs.isEmpty()) {\n                throw new FlowableException(\"No timer jobs found for plan item instance \" + eventListenerInstanceId);\n            }\n            \n            if (timerJobs.size() > 1) {\n                throw new FlowableException(\"Multiple timer jobs found for plan item instance \" + eventListenerInstanceId);\n            }\n            \n            timerJobId = timerJobs.get(0).getId();\n        \n        } else {\n            timerJobId = jobId;\n        }\n        \n        TimerJobService timerJobService = CommandContextUtil.getTimerJobService(commandContext);\n        TimerJobEntity timerJob = timerJobService.findTimerJobById(timerJobId);\n        if (timerJob == null) {\n            throw new FlowableObjectNotFoundException(\"Timer job not found for id \" + timerJobId);\n        }\n        \n        if (planItemInstance == null) {\n            planItemInstance = CommandContextUtil.getPlanItemInstanceEntityManager(commandContext).findById(timerJob.getSubScopeId());\n            if (planItemInstance == null) {\n                throw new FlowableException(\"Plan item instance not found for id \" + timerJob.getSubScopeId());","sourceCodeStart":50,"sourceCodeEnd":86,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/cmd/RescheduleTimerJobCmd.java#L50-L86","documentation":"RescheduleTimerJobCmd requires exactly one timer job for the event listener instance so it can deterministically reschedule it. When the query returns more than one job it throws FlowableException 'Multiple timer jobs found for plan item instance' because rescheduling an ambiguous set is unsafe.","triggerScenarios":"Multiple timer jobs existing for a single plan item instance id — caused by reschedule/duplicate logic bugs, repeated job creation during retries, or manually inserting jobs.","commonSituations":"Engines upgraded mid-flight leaving duplicated timer jobs; custom code creating additional timers for the same listener; async executor retry corner cases.","solutions":["Delete duplicate timer jobs so only one remains, then retry the reschedule","Reschedule by explicit jobId (the other code path) instead of by eventListenerInstanceId","Audit job creation code for accidental double insertion","Report/upgrade if duplicates stem from a known engine bug"],"exampleFix":"// before\nrescheduleTimer(eventListenerInstanceId, newDate); // throws if multiple jobs\n// after\nList<Job> jobs = jobService.createTimerJobQuery().planItemInstanceId(eventListenerInstanceId).list();\nfor (int i = 1; i < jobs.size(); i++) {\n    jobService.deleteJob(jobs.get(i).getId()); // dedupe extras\n}\nrescheduleTimer(eventListenerInstanceId, newDate);","handlingStrategy":"validation","validationCode":"long n = jobService.createTimerJobQuery().planItemInstanceId(eventListenerInstanceId).count();\nif (n != 1) throw new IllegalStateException(\"Expected exactly 1 timer job, found \" + n);","typeGuard":null,"tryCatchPattern":"try { reschedule(...); } catch (FlowableException e) { log.error(\"Ambiguous timer jobs for {}: {}\", eventListenerInstanceId, e.getMessage()); }","preventionTips":["Reschedule by explicit jobId when uniqueness cannot be guaranteed","Deduplicate timer jobs before rescheduling","Monitor for duplicate job creation as an engine/config regression"],"tags":["cmmn","timer-job","invariant"],"backgroundTag":"internal-invariant-violation","analyzedSha":"d6d39ce1c69ff244f2d9dc6af756a9b95e865586","analyzedAt":"2026-09-11T06:41:19.413Z","contentChangedAt":"2026-09-11T06:41:19.413Z","schemaVersion":2},"datasetVersion":"2026-09-18T11:17:12.947Z"}