flowable/flowable-engine · error · FlowableIllegalArgumentException

An end date can only be provided when rescheduling a timer u

Error message

An end date can only be provided when rescheduling a timer using timeDuration.

What it means

RescheduleTimerJobCmd only permits endDate when the timer is rescheduled with a repeating timeCycle... actually per the check: endDate is only allowed when timeCycle == null — but the message says timeDuration. The command throws when endDate != null while timeCycle is null and the caller did not supply a valid single timeDuration setup; effectively endDate is only meaningful for duration-based timers in this API path. It guards against nonsensical schedule combinations.

Source

Thrown at modules/flowable-engine/src/main/java/org/flowable/engine/impl/cmd/RescheduleTimerJobCmd.java:51

    private String timeDuration;
    private String timeCycle;
    private String endDate;
    private String calendarName;

    public RescheduleTimerJobCmd(String timerJobId, String timeDate, String timeDuration, String timeCycle, String endDate, String calendarName) {
        if (timerJobId == null) {
            throw new FlowableIllegalArgumentException("The timer job id is mandatory, but 'null' has been provided.");
        }

        int timeValues = Collections.frequency(Arrays.asList(timeDate, timeDuration, timeCycle), null);
        if (timeValues == 0) {
            throw new FlowableIllegalArgumentException("A non-null value is required for one of timeDate, timeDuration, or timeCycle");
        } else if (timeValues != 2) {
            throw new FlowableIllegalArgumentException("At most one non-null value can be provided for timeDate, timeDuration, or timeCycle");
        }

        if (endDate != null && timeCycle == null) {
            throw new FlowableIllegalArgumentException("An end date can only be provided when rescheduling a timer using timeDuration.");
        }

        this.timerJobId = timerJobId;
        this.timeDate = timeDate;
        this.timeDuration = timeDuration;
        this.timeCycle = timeCycle;
        this.endDate = endDate;
        this.calendarName = calendarName;
    }

    @Override
    public TimerJobEntity execute(CommandContext commandContext) {
        TimerEventDefinition ted = new TimerEventDefinition();
        ted.setTimeDate(timeDate);
        ted.setTimeDuration(timeDuration);
        ted.setTimeCycle(timeCycle);
        ted.setEndDate(endDate);
        ted.setCalendarName(calendarName);

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Remove endDate unless you are rescheduling with timeDuration.
  2. If you need a bounded repeating timer, express it via timeDuration plus endDate as supported by this command.
  3. Null the endDate field when passing timeCycle or timeDate.

Example fix

// before
new RescheduleTimerJobCmd(jobId, null, "PT1H", "R/PT1H", endDate);
// after
new RescheduleTimerJobCmd(jobId, null, "PT1H", null, endDate);
Defensive patterns

Strategy: validation

Validate before calling

if (endDate != null && (timeCycle != null || timeDuration == null)) {
    throw new IllegalArgumentException("endDate only allowed with timeDuration");
}

Try / catch

try {
    managementService.rescheduleTimeDurationJob(jobId, duration, endDate);
} catch (FlowableIllegalArgumentException e) {
    // drop endDate or switch to duration-based schedule
}

Prevention

When it happens

Trigger: Executing RescheduleTimerJobCmd with endDate non-null but timeCycle non-null (endDate + cycle), or endDate non-null with timeDate — any combination where the repeat/end semantics are not duration-driven.

Common situations: Developers wanting a timer to stop after a deadline copy the endDate parameter from timer definitions that used ISO 8601 repeat cycles, mixing endDate with timeCycle; or reschedule calls built generically from form data carrying a stale endDate.

Related errors


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