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

  1. 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.
  2. Convert at assignment: Date.from(instant) or Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant()).
  3. Adjust the expression to perform the conversion, e.g. ${dateTimeUtil.toDate(myValue)}.
  4. 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

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


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)