flowable/flowable-engine · error · ActivitiException

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

Error message

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

TimerJobEntity.restoreExtraData evaluates the timer's end-date expression when re-creating a timer (e.g. after restart or when scheduling repeating timers). The expression must evaluate to a java.util.Date or a String parseable by the business calendar; any other type makes the engine throw this ActivitiException naming the activity. It protects the job store from an unparseable due date.

Solutions

  1. Make the expression return a java.util.Date or an ISO-8601 'yyyy-MM-dd'T'hh:mm:ss' String
  2. Convert the variable at process start (store as Date or formatted String) instead of at timer evaluation time
  3. Fix the expression in the BPMN XML (e.g. ${endDate} → ${endDateAsString} backed by a correctly typed variable)
  4. Check for type changes after engine/version migration and re-deploy with corrected models

Example fix

// before: variable is a timestamp long
runtimeService.startProcessInstanceByKey("p", vars); // vars: endDate = 1727000000L
// after
SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
vars.put("endDate", fmt.format(new Date(1727000000L * 1000)));
runtimeService.startProcessInstanceByKey("p", vars);
Defensive patterns

Strategy: validation

Validate before calling

Object v = vars.get("endDate");
boolean ok = v instanceof java.util.Date || v instanceof String; // String must be ISO yyyy-MM-dd'T'HH:mm:ss

Type guard

boolean isValidTimerValue(Object v) {
    return v instanceof java.util.Date || v instanceof String;
}

Prevention

When it happens

Trigger: A boundary/intermediate timer's timeDate/timeDuration/timeCycle endDate expression evaluates to a type other than Date or String (e.g. Integer, Long, or a custom object) when the timer job is restored or scheduled by execute/scheduleNewTimer.

Common situations: Process variable passed into a timer endDate expression has the wrong type (number instead of ISO date string or Date); expression returns a Joda/Java-8 type the engine doesn't recognize; Activiti 5→Flowable migration where variable types changed; expression misconfigured in BPMN XML.

Understand the failure class

Background: "invalid duration" / "failed to parse duration": why your timeout, interval, or TTL string is rejected and which formats each library accepts — this error's family across 32 libraries.

Related errors


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

Appendix: source

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

                        .getBusinessCalendar(getBusinessCalendarName(TimerEventHandler.geCalendarNameFromConfiguration(jobHandlerConfiguration)));

                VariableScope executionEntity = null;
                if (executionId != null) {
                    executionEntity = commandContext.getExecutionEntityManager().findExecutionById(executionId);
                }

                if (executionEntity == null) {
                    executionEntity = NoExecutionVariableScope.getSharedInstance();
                }

                if (endDateExpression != null) {
                    Object endDateValue = endDateExpression.getValue(executionEntity);
                    if (endDateValue instanceof String) {
                        endDateString = (String) endDateValue;
                    } else if (endDateValue instanceof Date) {
                        endDate = (Date) endDateValue;
                    } else {
                        throw new ActivitiException("Timer '" + ((ExecutionEntity) 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'");
                    }

                    if (endDate == null) {
                        endDate = businessCalendar.resolveEndDate(endDateString);
                    }
                }
            }
        }

        if (processDefinitionId != null) {
            ProcessDefinition def = Context.getProcessEngineConfiguration().getRepositoryService().getProcessDefinition(processDefinitionId);
            maxIterations = checkStartEventDefinitions(def, embededActivityId);
            if (maxIterations <= 1) {
                maxIterations = checkBoundaryEventsDefinitions(def, embededActivityId);
            }
        } else {
            maxIterations = 1;

View on GitHub (pinned to d6d39ce1c6)