flowable/flowable-engine · error · FlowableException

Empty timer job can not be scheduled

Error message

Empty timer job can not be scheduled

What it means

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).

Source

Thrown at modules/flowable-job-service/src/main/java/org/flowable/job/service/impl/asyncexecutor/TimerJobSchedulerImpl.java:62

            if (newTimerJobEntity != null) {
                if (jobServiceConfiguration.getInternalJobManager() != null) {
                    jobServiceConfiguration.getInternalJobManager().preRepeatedTimerSchedule(newTimerJobEntity, variableScope);
                }

                scheduleTimerJob(newTimerJobEntity);
            }
        }
    }

    @Override
    public void scheduleTimerJob(TimerJobEntity timerJob) {
        scheduleTimer(timerJob);
        sendTimerScheduledEvent(timerJob);
    }

    protected void scheduleTimer(TimerJobEntity timerJob) {
        if (timerJob == null) {
            throw new FlowableException("Empty timer job can not be scheduled");
        }
        callJobProcessors(jobServiceConfiguration, JobProcessorContext.Phase.BEFORE_CREATE, timerJob);
        jobServiceConfiguration.getTimerJobEntityManager().insert(timerJob);
    }

    protected void sendTimerScheduledEvent(TimerJobEntity timerJob) {
        FlowableEventDispatcher eventDispatcher = jobServiceConfiguration.getEventDispatcher();
        if (eventDispatcher != null && eventDispatcher.isEnabled()) {
            eventDispatcher.dispatchEvent(FlowableJobEventBuilder.createEntityEvent(
                    FlowableEngineEventType.TIMER_SCHEDULED, timerJob), jobServiceConfiguration.getEngineName());
        }
    }
}

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Ensure the TimerJobEntity is created (jobServiceConfiguration.getTimerJobEntityManager().create()) before scheduleTimerJob calls scheduleTimer.
  2. Null-check the timer job in calling code before invoking the scheduler.
  3. If triggered by boundary/boundary-event timers, verify the activity and process instance still exist so entity creation succeeds.

Example fix

// before
TimerJobEntity timerJob = null; // creation skipped
scheduler.scheduleTimerJob(...timerJob...);

// after
TimerJobEntity timerJob = jobServiceConfiguration.getTimerJobEntityManager().create();
timerJob.setJobHandlerType(...);
// populate fields, then
scheduler.scheduleTimerJob(...timerJob...);
Defensive patterns

Strategy: type-guard

Validate before calling

if (timerJob == null) {
    throw new IllegalStateException("Refusing to schedule: timer job was not created");
}

Type guard

boolean isSchedulable(TimerJobEntity job) { return job != null; }

Try / catch

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; }

Prevention

When it happens

Trigger: scheduleTimerJob (e.g. triggered by executeTimerJob for an async-continuation timer) creates/obtains a TimerJobEntity that is null and passes it to scheduleTimer.

Common situations: 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.

Related errors


AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11). Data as JSON: /api/errors/998c0f4a6792e312. Report an issue: GitHub.