jeecgboot/JeecgBoot · error · ParseException
'L' not expected in seconds, minutes or hours fields.
Error message
'L' not expected in seconds, minutes or hours fields.
What it means
'L' means 'last' (last day of month, or last weekday). It is only meaningful for DAY_OF_MONTH and DAY_OF_WEEK. Because field types are numbered SECOND=0, MINUTE=1, HOUR=2, DAY_OF_MONTH=3, the guard 'type < DAY_OF_MONTH' rejects 'L' in the seconds, minutes or hours fields. Using 'L' there is a syntax error.
Source
Thrown at jeecg-boot/jeecg-server-cloud/jeecg-visual/jeecg-cloud-xxljob/src/main/java/com/xxl/job/admin/business/scheduler/cron/CronExpression.java:670
}
incr = getNumericValue(s, i);
i++;
if (incr > 10) {
i++;
}
checkIncrementRange(incr, type, i);
} else {
incr = 1;
}
addToSet(ALL_SPEC_INT, -1, incr, type);
return i;
} else if (c == 'L') {
if(type < DAY_OF_MONTH)
throw new ParseException("'L' not expected in seconds, minutes or hours fields.", i);
i++;
if (type == DAY_OF_WEEK) {
addToSet(7, 7, 0, type);
}
if (type == DAY_OF_MONTH) {
int dom = LAST_DAY_OFFSET_END;
boolean nearestWeekday = false;
if (s.length() > i) {
c = s.charAt(i);
if (c == '-') {
ValueSet vs = getValue(0, s, i + 1);
int offset = vs.value;
if (offset > MAX_LAST_DAY_OFFSET)
throw new ParseException("Offset from last day must be <= " + MAX_LAST_DAY_OFFSET, i + 1);
dom -= offset;
i = vs.pos;
}View on GitHub (pinned to 96fb33f5ec)
Solutions
- Use 'L' only in the day-of-month field (e.g. '0 0 0 L * ?' = last day of month at 00:00) or day-of-week field ('0 0 0 ? * L' = Saturday).
- For 'last second/minute/hour' there is no Quartz equivalent - express it numerically (59, 59, 23).
- Validate that 'L' appears only in dom (field 4) or dow (field 6) positions.
Example fix
// before - 'L' in the hour field String cron = "0 0 L * * ?"; // 'L' not expected in seconds, minutes or hours fields // after - 23 for last hour, L reserved for day-of-month String cron = "0 0 23 * * ?"; // or last day of month String cron = "0 0 0 L * ?";
Defensive patterns
Strategy: validation
Validate before calling
// 'L' is legal only in dom (index 3) or dow (index 5).
public static String validateLastModifierPlacement(String expr) {
String[] f = expr.trim().split("\\s+");
for (int i = 0; i < f.length && i < 6; i++) {
if (f[i].contains("L") && i != 3 && i != 5) {
return "'L' is only allowed in day-of-month or day-of-week, found in field " + (i + 1);
}
}
return null;
} Type guard
public static boolean lastModifierOnlyInDayFields(String[] fields) {
for (int i = 0; i < fields.length && i < 6; i++) {
if (fields[i].contains("L") && i != 3 && i != 5) return false;
}
return true;
} Try / catch
try {
new CronExpression(expr);
} catch (ParseException e) {
if (e.getMessage().contains("not expected in seconds")) {
return "'L' is for day fields only. Use 59/59/23 for last second/minute/hour. " + e.getMessage();
}
throw e;
} Prevention
- Forbid 'L' outside the dom/dow positions at the input boundary.
- Use explicit numeric maxima (59, 59, 23) for last-second/minute/hour needs.
- Document for users that 'L' means 'last day of month / last weekday'.
When it happens
Trigger: An 'L' in an early field: 'L * * * * ?' (SECOND), '0 L * * * ?' (MINUTE), '0 0 L * * ?' is valid (HOUR would be invalid too: '0 0 L * * ?' -> HOUR='L' triggers since HOUR=2 < 3). The 'L' for hours is sometimes confused with a 'last hour' concept that Quartz does not support.
Common situations: Assuming 'L' means 'last X' for any unit; copy/paste of an 'L' template into the wrong field; UI that lets users pick 'last' without scoping it to day fields.
Related errors
- Illegal characters for this position: '{sub}'
- '?' can only be specified for Day-of-Month or Day-of-Week.
- Offset from last day must be <= {MAX_LAST_DAY_OFFSET}
- Illegal cron expression format ({e})
- Invalid Month value: '{sub}'
AI-assisted analysis of jeecgboot/JeecgBoot@96fb33f5ec (2026-08-14).
Data as JSON: /api/errors/e1f38602233ec360.
Report an issue: GitHub.