alibaba/spring-cloud-alibaba · error · ParseException

Offset from last day must be <= 30

Error message

Offset from last day must be <= 30

What it means

Thrown when the 'L' (last day) construct in the DAY_OF_MONTH field is given an offset larger than 30 via 'L-N' (guard at CronExpression.java:471-472). 'L-N' means 'N days before the last day of the month', so an offset beyond 30 is meaningless.

Source

Thrown at spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-schedulerx/src/main/java/com/alibaba/cloud/scheduling/schedulerx/util/CronExpression.java:472

			addToSet(ALL_SPEC_INT, -1, incr, type);
			return i;
		}
		else if (c == 'L') {
			i++;
			if (type == DAY_OF_MONTH) {
				lastdayOfMonth = true;
			}
			if (type == DAY_OF_WEEK) {
				addToSet(7, 7, 0, type);
			}
			if (type == DAY_OF_MONTH && s.length() > i) {
				c = s.charAt(i);
				if (c == '-') {
					final ValueSet vs = getValue(0, s, i + 1);
					lastdayOffset = vs.value;
					if (lastdayOffset > 30) {
						throw new ParseException("Offset from last day must be <= 30", i + 1);
					}
					i = vs.pos;
				}
				if (s.length() > i) {
					c = s.charAt(i);
					if (c == 'W') {
						nearestWeekday = true;
						i++;
					}
				}
			}
			return i;
		}
		else if (c >= '0' && c <= '9') {
			int val = Integer.parseInt(String.valueOf(c));
			i++;
			if (i >= s.length()) {
				addToSet(val, -1, -1, type);

View on GitHub (pinned to 115d590110)

Solutions

  1. Use an offset <= 30, e.g. 'L-31' -> 'L-5' for the 5th-to-last day.
  2. If you need a range of final days, enumerate them or use a step, not a large 'L-N'.
  3. Sanitize any templated offset value before injecting into the cron string.

Example fix

// before
new CronExpression("0 0 0 L-31 * ?");
// after
new CronExpression("0 0 0 L-5 * ?"); // 5 days before month end
Defensive patterns

Strategy: validation

Validate before calling

// 'L-N' offset must be 0..30.
static boolean lastDayOffsetOk(String domField) {
    if (!domField.startsWith("L-")) return true;
    try {
        int off = Integer.parseInt(domField.substring(2));
        return off >= 0 && off <= 30;
    } catch (NumberFormatException e) { return false; }
}

Try / catch

try {
    CronExpression cron = new CronExpression(raw);
} catch (ParseException e) {
    throw new IllegalArgumentException("Invalid cron expression: " + raw, e);
}

Prevention

When it happens

Trigger: `new CronExpression("0 0 0 L-31 * ?")` (last-day offset 31). Any 'L-NN' in the day-of-month field where NN > 30.

Common situations: Developer misreads 'L-N' as 'last N days' or typos the offset; also from concatenating an unbounded value into the offset.

Related errors


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