flowable/flowable-engine · error · ActivitiIllegalArgumentException

Timer ' ' was not configured with a valid duration/time

Error message

Timer '${activityId}' was not configured with a valid duration/time

What it means

TimerDeclarationImpl.prepareTimerEntity resolves the timer description from a field/expression; if the resolved description is null, the business calendar would NPE later, so the engine throws ActivitiIllegalArgumentException early stating the timer activity has no valid duration/time configured. It guards the case where the timeDuration/timeDate expression evaluates to nothing.

Solutions

  1. Set the variable referenced by the timer expression before reaching the timer activity
  2. Use a literal ISO-8601 duration in the BPMN (e.g. PT10M) instead of a nullable expression
  3. Fix the expression/bean so it always returns a non-null Date or duration string
  4. Add a default in the expression, e.g. ${dueDate != null ? dueDate : 'PT1H'}

Example fix

// before (BPMN)
<timeDuration>${delayDuration}</timeDuration>  // variable never set
// after
runtimeService.setVariable(executionId, "delayDuration", "PT30M");
// or: <timeDuration>PT30M</timeDuration>
Defensive patterns

Strategy: validation

Validate before calling

Object v = runtimeService.getVariable(executionId, "delayDuration");
if (v == null) throw new IllegalStateException("Timer duration variable must be set before reaching the timer");

Type guard

boolean hasTimerValue(VariableScope scope, Expression timerExpr) {
    return timerExpr != null && timerExpr.getValue(scope) != null;
}

Try / catch

try {
    processEngine.getRuntimeService().startProcessInstanceByKey(key, vars);
} catch (ActivitiIllegalArgumentException e) {
    if (e.getMessage() != null && e.getMessage().contains("valid duration/time")) { /* fix timer config */ }
    else throw e;
}

Prevention

When it happens

Trigger: A timer event definition whose timeDuration/timeDate/timeCycle expression evaluates to null at runtime, e.g. ${missingVar} referencing an unset variable, and no valid ISO-8601 duration is available.

Common situations: Variable used in the timer expression not set before the flow reaches the timer; typo in expression variable; conditional timer definition where the field resolves to null; wrong expression returning void/null.

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/8ba7764eec48701e. Report an issue: GitHub.

Appendix: source

Thrown at modules/flowable5-engine/src/main/java/org/activiti/engine/impl/jobexecutor/TimerDeclarationImpl.java:134

        // evaluating variables but other context, evaluating should happen nevertheless
        VariableScope scopeForExpression = executionEntity;
        if (scopeForExpression == null) {
            scopeForExpression = NoExecutionVariableScope.getSharedInstance();
        }

        String calendarNameValue = type.calendarName;
        if (this.calendarNameExpression != null) {
            calendarNameValue = (String) this.calendarNameExpression.getValue(scopeForExpression);
        }

        BusinessCalendar businessCalendar = Context
                .getProcessEngineConfiguration()
                .getBusinessCalendarManager()
                .getBusinessCalendar(calendarNameValue);

        if (description == null) {
            // Prevent NPE from happening in the next line
            throw new ActivitiIllegalArgumentException("Timer '" + executionEntity.getActivityId() + "' was not configured with a valid duration/time");
        }

        String endDateString = null;
        String dueDateString = null;
        Date duedate = null;
        Date endDate = null;

        if (endDateExpression != null && !(scopeForExpression instanceof NoExecutionVariableScope)) {
            Object endDateValue = endDateExpression.getValue(scopeForExpression);
            if (endDateValue instanceof String) {
                endDateString = (String) endDateValue;
            } else if (endDateValue instanceof Date) {
                endDate = (Date) endDateValue;
            } else if (endDateValue instanceof DateTime) {
                // Joda DateTime support
                duedate = ((DateTime) endDateValue).toDate();
            } else {
                throw new ActivitiException("Timer '" + executionEntity.getActivityId() + "' was not configured with a valid duration/time, either hand in a java.util.Date or a String in format 'yyyy-MM-dd'T'hh:mm:ss'");

View on GitHub (pinned to d6d39ce1c6)