{"record":{"id":"998c0f4a6792e312","repo":"flowable/flowable-engine","slug":"empty-timer-job-can-not-be-scheduled-998c0f","errorCode":null,"errorMessage":"Empty timer job can not be scheduled","messagePattern":"Empty timer job can not be scheduled","errorType":"exception","errorClass":"FlowableException","httpStatus":null,"severity":"error","filePath":"modules/flowable-job-service/src/main/java/org/flowable/job/service/impl/asyncexecutor/TimerJobSchedulerImpl.java","lineNumber":62,"sourceCode":"            if (newTimerJobEntity != null) {\n                if (jobServiceConfiguration.getInternalJobManager() != null) {\n                    jobServiceConfiguration.getInternalJobManager().preRepeatedTimerSchedule(newTimerJobEntity, variableScope);\n                }\n\n                scheduleTimerJob(newTimerJobEntity);\n            }\n        }\n    }\n\n    @Override\n    public void scheduleTimerJob(TimerJobEntity timerJob) {\n        scheduleTimer(timerJob);\n        sendTimerScheduledEvent(timerJob);\n    }\n\n    protected void scheduleTimer(TimerJobEntity timerJob) {\n        if (timerJob == null) {\n            throw new FlowableException(\"Empty timer job can not be scheduled\");\n        }\n        callJobProcessors(jobServiceConfiguration, JobProcessorContext.Phase.BEFORE_CREATE, timerJob);\n        jobServiceConfiguration.getTimerJobEntityManager().insert(timerJob);\n    }\n\n    protected void sendTimerScheduledEvent(TimerJobEntity timerJob) {\n        FlowableEventDispatcher eventDispatcher = jobServiceConfiguration.getEventDispatcher();\n        if (eventDispatcher != null && eventDispatcher.isEnabled()) {\n            eventDispatcher.dispatchEvent(FlowableJobEventBuilder.createEntityEvent(\n                    FlowableEngineEventType.TIMER_SCHEDULED, timerJob), jobServiceConfiguration.getEngineName());\n        }\n    }\n}\n","sourceCodeStart":44,"sourceCodeEnd":76,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-job-service/src/main/java/org/flowable/job/service/impl/asyncexecutor/TimerJobSchedulerImpl.java#L44-L76","documentation":"TimerJobSchedulerImpl.scheduleTimer inserts a TimerJobEntity into the timer job store, but first guards against a null timer job with this FlowableException. A null timer job cannot be persisted or scheduled, and this indicates a bug in the code path that created it (e.g. scheduleTimerJob failing to create the entity).","triggerScenarios":"scheduleTimerJob (e.g. triggered by executeTimerJob for an async-continuation timer) creates/obtains a TimerJobEntity that is null and passes it to scheduleTimer.","commonSituations":"Custom job creation code returning null; entity manager stubs in tests returning null; timer jobs for deleted process instances where the create step silently skipped entity creation.","solutions":["Ensure the TimerJobEntity is created (jobServiceConfiguration.getTimerJobEntityManager().create()) before scheduleTimerJob calls scheduleTimer.","Null-check the timer job in calling code before invoking the scheduler.","If triggered by boundary/boundary-event timers, verify the activity and process instance still exist so entity creation succeeds."],"exampleFix":"// before\nTimerJobEntity timerJob = null; // creation skipped\nscheduler.scheduleTimerJob(...timerJob...);\n\n// after\nTimerJobEntity timerJob = jobServiceConfiguration.getTimerJobEntityManager().create();\ntimerJob.setJobHandlerType(...);\n// populate fields, then\nscheduler.scheduleTimerJob(...timerJob...);","handlingStrategy":"type-guard","validationCode":"if (timerJob == null) {\n    throw new IllegalStateException(\"Refusing to schedule: timer job was not created\");\n}","typeGuard":"boolean isSchedulable(TimerJobEntity job) { return job != null; }","tryCatchPattern":"try { scheduler.scheduleTimerJob(...); } catch (FlowableException e) { if (e.getMessage().equals(\"Empty timer job can not be scheduled\")) { timerJob = recreateTimerJob(context); scheduler.scheduleTimerJob(...timerJob...); } else throw e; }","preventionTips":["Always create TimerJobEntity via the timer job entity manager before scheduling.","Null-check entities returned by custom factory/test stubs.","Verify process instance and activity exist before scheduling boundary timers.","Unit-test timer creation paths end-to-end with a real entity manager."],"tags":["flowable","timer-job","null-argument","scheduler"],"backgroundTag":"null-argument","analyzedSha":"d6d39ce1c69ff244f2d9dc6af756a9b95e865586","analyzedAt":"2026-09-11T06:41:19.413Z","contentChangedAt":"2026-09-11T06:41:19.413Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}