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
- 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)
- If you have epoch millis or java.time types, convert explicitly: new java.util.Date(millis) or Date.from(instant)
- Check that the variable referenced by the timer expression is initialized and of the expected type in the execution scope
- 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
- Only store java.util.Date or ISO-format strings in variables referenced by timer expressions
- Convert epoch millis / java.time types (Instant, ZonedDateTime) to Date before assigning
- Initialize timer end-date variables before the timer is created in the flow
- Test timer definitions (cycle/duration/end date) in a small deployment before production
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
- Skip expression variable does not resolve to a boolean. " +
- Skip expression does not resolve to a boolean: " + skipExpre
- Custom properties resolver delegate expression " + expressio
- Delegate expression " + expression + " did not resolve to an
- Delegate expression " + expression + " did not resolve to an
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/773bd630c168853f.
Report an issue: GitHub.