flowable/flowable-engine · error · FlowableException

Timer '{activityId}' in {variableScope} was not configured w

Error message

Timer '{activityId}' in {variableScope} 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'

What it means

While preparing to delete/reschedule a timer job, DefaultInternalJobManager evaluates the timer's end-date expression against the variable scope and expects the result to be a java.util.Date or a date String. If the expression evaluates to some other type (or an unusable value), the engine throws this FlowableException naming the activity and variable scope. It indicates the timer definition (timer end-date expression) produced an incompatible value.

Source

Thrown at modules/flowable-engine/src/main/java/org/flowable/engine/impl/cfg/DefaultInternalJobManager.java:258

            activityId = TimerEventHandler.getActivityIdFromConfiguration(jobEntity.getJobHandlerConfiguration());
            String endDateExpressionString = TimerEventHandler.getEndDateFromConfiguration(jobEntity.getJobHandlerConfiguration());

            if (endDateExpressionString != null) {
                Expression endDateExpression = processEngineConfiguration.getExpressionManager().createExpression(endDateExpressionString);

                String endDateString = null;

                BusinessCalendar businessCalendar = processEngineConfiguration.getBusinessCalendarManager().getBusinessCalendar(
                        getBusinessCalendarName(TimerEventHandler.getCalendarNameFromConfiguration(jobEntity.getJobHandlerConfiguration()), variableScope));

                if (endDateExpression != null) {
                    Object endDateValue = endDateExpression.getValue(variableScope);
                    if (endDateValue instanceof String) {
                        endDateString = (String) endDateValue;
                    } else if (endDateValue instanceof Date) {
                        jobEntity.setEndDate((Date) endDateValue);
                    } else {
                        throw new FlowableException("Timer '" + ((ExecutionEntity) variableScope).getActivityId()
                                + "' in " + variableScope + " 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'");
                    }

                    if (jobEntity.getEndDate() == null) {
                        jobEntity.setEndDate(businessCalendar.resolveEndDate(endDateString));
                    }
                }
            }
        }

        int maxIterations = 1;
        if (jobEntity.getProcessDefinitionId() != null) {
            org.flowable.bpmn.model.Process process = ProcessDefinitionUtil.getProcess(jobEntity.getProcessDefinitionId());
            maxIterations = getMaxIterations(process, activityId);
            if (maxIterations <= 1) {
                maxIterations = getMaxIterations(process, activityId);
            }
        }

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Ensure the endDate expression evaluates to java.util.Date or a String in ISO format yyyy-MM-dd'T'hh:mm:ss (convert in code before setting the variable)
  2. If you have epoch millis or java.time types, convert explicitly: new java.util.Date(millis) or Date.from(instant)
  3. Check that the variable referenced by the timer expression is initialized and of the expected type in the execution scope
  4. Test the timer expression in isolation (variable reference vs literal) to confirm what type it returns

Example fix

// before
execution.setVariable("endDate", System.currentTimeMillis());

// after
execution.setVariable("endDate", new java.util.Date());
// or a String:
execution.setVariable("endDate", "2026-09-10T12:00:00");
Defensive patterns

Strategy: type-guard

Validate before calling

// before setting the variable that drives the timer end date
Object v = execution.getVariable("endDate");
if (!(v instanceof java.util.Date) && !(v instanceof String)) {
  throw new IllegalArgumentException("endDate must be Date or ISO string yyyy-MM-dd'T'hh:mm:ss, got "
      + (v == null ? "null" : v.getClass().getName()));
}

Type guard

static boolean isValidTimerDate(Object v) {
  if (v instanceof java.util.Date) return true;
  if (v instanceof String) {
    try {
      new java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss").parse((String) v);
      return true;
    } catch (Exception e) {
      return false;
    }
  }
  return false;
}

Try / catch

try {
  runtimeService.setVariable(executionId, "endDate", value);
} catch (FlowableException e) {
  if (e.getMessage() != null && e.getMessage().contains("was not configured with a valid duration/time")) {
    runtimeService.setVariable(executionId, "endDate", new java.util.Date());
  } else {
    throw e;
  }
}

Prevention

When it happens

Trigger: A timer job with an endDate expression whose getValue(variableScope) returns neither String nor Date — e.g. a process variable holding a Long/Integer timestamp, a ZonedDateTime/OffsetDateTime, or an uninitialized variable flowing through the wrong branch — evaluated in preTimerJobDeleteInternal.

Common situations: Setting the timer end-date variable to numeric epoch millis instead of a Date; using java.time types (Instant/ZonedDateTime) which Flowable's expression handling does not convert here; a boundary timer cycle whose end condition variable was never initialized; an EL/Spring expression returning a computed value of the wrong type.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


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