flowable/flowable-engine · error · ActivitiIllegalArgumentException

duedate is null

Error message

duedate is null

What it means

JobEntityManager.schedule persists a timer job and requires a valid due date, since the async executor depends on it to decide when to fire. A timer with a null duedate is rejected with ActivitiIllegalArgumentException before insertion.

Solutions

  1. Ensure the timer definition provides a valid timeDate/timeDuration/timeCycle expression and that referenced process variables are set.
  2. Set duedate explicitly (e.g., new Date(System.currentTimeMillis() + delay)) before calling schedule.
  3. Validate/parse the timer expression at deployment time so failures surface at deploy, not runtime.

Example fix

// before
TimerJobEntity timer = TimerJobEntity.createAndInsertJob(job);
schedule(timer); // duedate null -> throws

// after
TimerJobEntity timer = TimerJobEntity.createAndInsertJob(job);
timer.setDuedate(new Date(System.currentTimeMillis() + Duration.ofMinutes(5).toMillis()));
schedule(timer);
Defensive patterns

Strategy: validation

Validate before calling

if (timer.getDuedate() == null) {
    timer.setDuedate(computeDuedateFromDefinition(timer));
}
schedule(timer);

Type guard

function isSchedulable(timer) {
  return timer.getDuedate() instanceof Date;
}

Try / catch

try {
    jobEntityManager.schedule(timer);
} catch (ActivitiIllegalArgumentException e) {
    log.error("Timer {} has no duedate; check its time expression/variables", timer.getId());
}

Prevention

When it happens

Trigger: Programmatically creating a TimerJobEntity and calling schedule without setting duedate; a boundary/intermediate timer event whose timeDuration/timeDate expression evaluated to null; custom job creation code copied from examples and partially filled.

Common situations: BPMN timer events using an expression variable that resolves to null at runtime (unset process variable); custom schedulers building timer entities manually; migrating job definitions where the due-date column/logic was dropped.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


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

Appendix: source

Thrown at modules/flowable5-engine/src/main/java/org/activiti/engine/impl/persistence/entity/JobEntityManager.java:57

/**
 * @author Tom Baeyens
 * @author Daniel Meyer
 * @author Joram Barrez
 */
public class JobEntityManager extends AbstractManager {

    public void send(JobEntity message) {
        message.insert();
        if (Context.getProcessEngineConfiguration().getAsyncExecutor().isActive()) {
            hintAsyncExecutor(message);
        }
    }

    public void schedule(TimerJobEntity timer) {
        Date duedate = timer.getDuedate();
        if (duedate == null) {
            throw new ActivitiIllegalArgumentException("duedate is null");
        }

        timer.insert();
    }

    protected void hintAsyncExecutor(Job job) {

        AsyncExecutor asyncExecutor = Context.getProcessEngineConfiguration().getAsyncExecutor();
        CommandContext commandContext = CommandContextUtil.getCommandContext();

        TransactionContext transactionContext = org.flowable.common.engine.impl.context.Context.getTransactionContext();
        if (transactionContext != null) {
            JobAddedTransactionListener jobAddedTransactionListener = new JobAddedTransactionListener(job, asyncExecutor,
                CommandContextUtil.getProcessEngineConfiguration(commandContext).getCommandExecutor());
            transactionContext.addTransactionListener(TransactionState.COMMITTED, jobAddedTransactionListener);

        } else {
            CommandContextCloseListener commandContextCloseListener = new AsyncJobAddedNotification(job, asyncExecutor);

View on GitHub (pinned to d6d39ce1c6)