alibaba/spring-cloud-alibaba · error · IllegalArgumentException
Start year must be less than stop year
Error message
Start year must be less than stop year
What it means
Thrown from the range-handling path of CronExpression.set(...) when the YEAR field is given a reverse or zero-width range. For calendar fields like hours or months a start greater than stop means 'wrap around', but a year has no wrap semantics, so the parser refuses it with an IllegalArgumentException. This protects the scheduler from an expression that can never resolve to a forward-moving fire time.
Source
Thrown at spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-schedulerx/src/main/java/com/alibaba/cloud/scheduling/schedulerx/util/CronExpression.java:893
max = 60;
break;
case MINUTE:
max = 60;
break;
case HOUR:
max = 24;
break;
case MONTH:
max = 12;
break;
case DAY_OF_WEEK:
max = 7;
break;
case DAY_OF_MONTH:
max = 31;
break;
case YEAR:
throw new IllegalArgumentException("Start year must be less than stop year");
default:
throw new IllegalArgumentException("Unexpected type encountered");
}
stopAt += max;
}
for (int i = startAt; i <= stopAt; i += incr) {
if (max == -1) {
// ie: there's no max to overflow over
set.add(i);
}
else {
// take the modulus to get the real value
int i2 = i % max;
// 1-indexed ranges should not include 0, and should include their max
if (i2 == 0 && (type == MONTH || type == DAY_OF_WEEK || type == DAY_OF_MONTH)) {
i2 = max;View on GitHub (pinned to 115d590110)
Solutions
- Order the year range so start < stop, e.g. '2020-2025'.
- If a single year is intended, use a scalar year ('2025') instead of a range.
- Validate generated cron strings with a unit test asserting the year field is ascending before registering the job.
Example fix
// before
new CronExpression("0 0 12 1 1 ? 2030-2020");
// after
new CronExpression("0 0 12 1 1 ? 2020-2030"); Defensive patterns
Strategy: validation
Validate before calling
void checkYearRange(String cron7) {
String[] f = cron7.split("\\s+");
if (f.length == 7 && f[6].matches("\\d{4}-\\d{4}")) {
String[] y = f[6].split("-");
if (Integer.parseInt(y[0]) >= Integer.parseInt(y[1]))
throw new IllegalArgumentException("Year range must be ascending: " + f[6]);
}
} Try / catch
try { new CronExpression(cron); }
catch (IllegalArgumentException | java.text.ParseException e) { log.error("Bad cron {}", cron, e); throw e; } Prevention
- Always order year ranges ascending.
- Prefer a scalar year over a single-year range.
- Unit-test dynamically generated cron strings.
When it happens
Trigger: A 7-field cron with a year range whose start is not strictly less than its stop, e.g. '0 0 12 1 1 ? 2025-2025' or '0 0 12 1 1 ? 2030-2020'. Also reachable via programmatic CronExpression construction that passes a descending year range.
Common situations: Swapping two years by mistake when writing a one-off scheduled job. Generating cron strings dynamically and not ordering the bounds. Assuming reverse ranges wrap like month/day fields do.
Related errors
- cronExpression cannot be null
- Support for specifying 'L' and 'LW' with other days of the m
- Support for specifying 'L' with other days of the week is no
- Support for specifying multiple "nth" days is not implemente
- Unexpected end of expression.
AI-assisted analysis of alibaba/spring-cloud-alibaba@115d590110 (2026-08-14).
Data as JSON: /api/errors/45048377e575887c.
Report an issue: GitHub.