flowable/flowable-engine · error · FlowableException
Timer for was not configured with a valid duration/time…
Error message
Timer for ${variableContainer} was not configured with a valid duration/time, either hand in a java.util.Date, java.time.LocalDate, java.time.LocalDateTime or a java.time.Instant or a org.joda.time.DateTime or a String in format 'yyyy-MM-dd'T'hh:mm:ss' What it means
TimerUtil.createTimerEntity evaluates the timer's due-date expression and accepts only java.util.Date, LocalDate, LocalDateTime, Instant, org.joda.time.DateTime, or a parseable date String. Any other non-null value is rejected with this FlowableException because no due date can be derived.
Solutions
- Store the variable as java.util.Date, LocalDate, LocalDateTime, Instant, or DateTime, or as a String in 'yyyy-MM-dd'T'hh:mm:ss' format.
- Convert at assignment: Date.from(instant) or Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant()).
- Adjust the expression to perform the conversion, e.g. ${dateTimeUtil.toDate(myValue)}.
- If using ZonedDateTime, convert it: Date.from(zdt.toInstant()) before the timer sees it.
Example fix
// before
execution.setVariable("dueAt", System.currentTimeMillis());
// after
execution.setVariable("dueAt", new Date()); Defensive patterns
Strategy: type-guard
Validate before calling
Object v = execution.getVariable("dueDateVar");
boolean ok = v instanceof Date || v instanceof LocalDate || v instanceof LocalDateTime || v instanceof Instant || v instanceof DateTime || (v instanceof String);
if (!ok) throw new IllegalArgumentException("timer dueDate variable must be Date/LocalDate/LocalDateTime/Instant/DateTime/String"); Type guard
boolean isSupportedDueDate(Object o) { return o == null || o instanceof Date || o instanceof LocalDate || o instanceof LocalDateTime || o instanceof Instant || o instanceof org.joda.time.DateTime || o instanceof String; } Try / catch
try { /* trigger timer */ } catch (FlowableException e) { if (e.getMessage().contains("was not configured with a valid duration/time")) log.error("Bad timer date type: {}", e.getMessage()); else throw e; } Prevention
- Store timer dates as java.util.Date (or the documented ISO string)
- Convert epoch millis/ZonedDateTime before setting the process variable
- Type-check variables in custom expression functions that feed timers
When it happens
Trigger: A timer definition's timeDate (or resolved dueDateValue) expression returns e.g. a Long timestamp, Integer, Calendar, or an arbitrary object that matches none of the supported instanceof branches in createTimerEntity.
Common situations: Setting the timer date variable to epoch millis (Long) instead of Date; process variables holding dates as Strings in non-'yyyy-MM-dd'T'hh:mm:ss' formats; using ZonedDateTime which is not in the supported list; values produced by custom expression functions.
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
- Timer ' ' in was not configured with a valid duration/time…
- App resource is not of type AppModel
- availableAfter is null
- availableBefore is null
- Can only move a history job to a history job
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/b97760c094a8fd84.
Report an issue: GitHub.
Appendix: source
Thrown at modules/flowable-engine/src/main/java/org/flowable/engine/impl/util/TimerUtil.java:189
"Using Joda-Time DateTime has been deprecated and will be removed in a future version. Timer event listener expression {} in {} resolved to a Joda-Time DateTime. ",
expression.getExpressionText(), variableContainer);
// JodaTime support
duedate = ((DateTime) dueDateValue).toDate();
} else if (dueDateValue instanceof Duration) {
dueDateString = ((Duration) dueDateValue).toString();
} else if (dueDateValue instanceof Instant) {
duedate = Date.from((Instant) dueDateValue);
} else if (dueDateValue instanceof LocalDate) {
duedate = Date.from(((LocalDate) dueDateValue).atStartOfDay().atZone(ZoneId.systemDefault()).toInstant());
} else if (dueDateValue instanceof LocalDateTime) {
duedate = Date.from(((LocalDateTime) dueDateValue).atZone(ZoneId.systemDefault()).toInstant());
} else if (dueDateValue != null) {
throw new FlowableException(
"Timer for " + variableContainer + " was not configured with a valid duration/time, either hand in a java.util.Date, java.time.LocalDate, java.time.LocalDateTime or a java.time.Instant or a org.joda.time.DateTime or a String in format 'yyyy-MM-dd'T'hh:mm:ss'");
}
if (duedate == null && dueDateString != null) {
duedate = businessCalendar.resolveDuedate(dueDateString);
}
TimerJobEntity timer = null;
if (duedate != null) {
timer = processEngineConfiguration.getJobServiceConfiguration().getTimerJobService().createTimerJob();
timer.setJobType(JobEntity.JOB_TYPE_TIMER);
timer.setRevision(1);
timer.setJobHandlerType(jobHandlerType);
timer.setJobHandlerConfiguration(jobHandlerConfig);
timer.setExclusive(true);
timer.setRetries(processEngineConfiguration.getAsyncExecutorNumberOfRetries());
timer.setDuedate(duedate);View on GitHub (pinned to d6d39ce1c6)