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

  1. Order the year range so start < stop, e.g. '2020-2025'.
  2. If a single year is intended, use a scalar year ('2025') instead of a range.
  3. 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

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


AI-assisted analysis of alibaba/spring-cloud-alibaba@115d590110 (2026-08-14). Data as JSON: /api/errors/45048377e575887c. Report an issue: GitHub.