flowable/flowable-engine · error · FlowableException
Empty timer job can not be scheduled
Error message
Empty timer job can not be scheduled
What it means
DefaultJobManager.moveTimerJobToExecutableJob promotes a TimerJobEntity to a runnable JobEntity when its due date arrives. A null timer job has nothing to schedule, so the method throws this FlowableException as a fail-fast precondition before creating the executable job.
Solutions
- Check timerJob != null before calling moveTimerJobToExecutableJob and skip/log instead
- Fix the upstream query/acquisition code that produced a null timer job
- Wrap the call and treat FlowableException as an indicator of a lifecycle race, then re-fetch
Example fix
// before
jobManager.moveTimerJobToExecutableJob(timerJob); // may be null
// after
if (timerJob != null) {
jobManager.moveTimerJobToExecutableJob(timerJob);
} Defensive patterns
Strategy: validation
Validate before calling
if (timerJob == null) { log.warn("No timer job to move to executable"); return; } Type guard
boolean canSchedule(TimerJobEntity t) { return t != null && t.getId() != null; } Try / catch
try { jobManager.moveTimerJobToExecutableJob(timerJob); } catch (FlowableException e) { log.warn("Timer job became null/absent before move: {}", e.getMessage()); } Prevention
- Null-check entities returned by timer-job queries before moving
- Handle race conditions where jobs are deleted between fetch and move
- Fail fast on null inputs at API boundaries
When it happens
Trigger: Passing null to JobManager.moveTimerJobToExecutableJob(timerJob), typically when a timer lookup/acquisition returned null and the result was forwarded without a null check.
Common situations: Custom async executor or timer acquisition logic that iterates results of a timer-job query and doesn't skip nulls; race conditions where the timer job was deleted between fetch and move.
Related errors
- Empty external worker job can not be scheduled
- Null job provided
- activity tenant id is null
- activityId is null
- after time is null
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/74ad1549eb4643e4.
Report an issue: GitHub.
Appendix: source
Thrown at modules/flowable-job-service/src/main/java/org/flowable/job/service/impl/asyncexecutor/DefaultJobManager.java:129
String category = jobEntity.getCategory();
if (StringUtils.isEmpty(category)) {
// If the job has no category then we should not hint it, another node needs to run it
return false;
}
// Finally, the job should be hinted if the enabled job categories contain the job category
return enabledJobCategories.contains(category);
}
@Override
public void scheduleTimerJob(TimerJobEntity timerJob) {
jobServiceConfiguration.getTimerJobScheduler().scheduleTimerJob(timerJob);
}
@Override
public JobEntity moveTimerJobToExecutableJob(TimerJobEntity timerJob) {
if (timerJob == null) {
throw new FlowableException("Empty timer job can not be scheduled");
}
JobEntity executableJob = createExecutableJobFromOtherJob(timerJob);
boolean insertSuccessful = jobServiceConfiguration.getJobEntityManager().insertJobEntity(executableJob);
if (insertSuccessful) {
jobServiceConfiguration.getTimerJobEntityManager().delete(timerJob);
triggerExecutorIfNeeded(executableJob);
return executableJob;
}
return null;
}
@Override
public void bulkMoveTimerJobsToExecutableJobs(List<TimerJobEntity> timerJobEntities) {
if (timerJobEntities == null || timerJobEntities.isEmpty()) {
throw new FlowableException("Empty timer jobs collection can not be scheduled");
}View on GitHub (pinned to d6d39ce1c6)