xuxueli/xxl-job · error · ParseException
'L' option is not valid here. (pos={})
Error message
'L' option is not valid here. (pos={}) What it means
In checkNext, when 'L' follows a numeric value in a field that is NOT Day-of-Week, this error fires. The 'NL' syntax (Nth last weekday) is only valid in the day-of-week field. In the main parse loop, standalone 'L' at the start of a field is handled separately (for day-of-month at line 667); this specific path handles 'L' appearing after a digit.
Source
Thrown at xxl-job-admin/src/main/java/com/xxl/job/admin/business/scheduler/cron/CronExpression.java:759
throws ParseException {
int end = -1;
int i = pos;
if (i >= s.length()) {
addToSet(val, end, -1, type);
return i;
}
char c = s.charAt(pos);
if (c == 'L') {
if (type == DAY_OF_WEEK) {
if(val < 1 || val > 7)
throw new ParseException("Day-of-Week values must be between 1 and 7", -1);
lastDayOfWeek = true;
} else {
throw new ParseException("'L' option is not valid here. (pos=" + i + ")", i);
}
TreeSet<Integer> set = getSet(type);
set.add(val);
i++;
return i;
}
if (c == 'W') {
if (type != DAY_OF_MONTH) {
throw new ParseException("'W' option is not valid here. (pos=" + i + ")", i);
}
if(val > 31)
throw new ParseException("The 'W' option does not make sense with values larger than 31 (max number of days in a month)", i);
nearestWeekdays.add(val);
i++;
return i;
}
View on GitHub (pinned to e74c784f68)
Solutions
- For 'last day of the month' in day-of-month, use standalone 'L' not '15L': '0 0 0 L * ?'.
- For 'last occurrence of a weekday' in day-of-week, use 'NL' with N in 1-7: '0 0 0 ? * 6L'.
- Move the 'L' modifier to the correct field.
Example fix
// before "0 0 0 5L * ?" // after "0 0 0 L * ?"
Defensive patterns
Strategy: validation
Validate before calling
// Reject 'NL' (number followed by L) in any field except day-of-week (index 5)
String[] parts = cron.trim().split("\\s+");
for (int idx = 0; idx < parts.length; idx++) {
if (parts[idx].matches(".*\\d+L.*") && idx != 5) {
throw new IllegalArgumentException("'NL' is only valid in day-of-week (field 5): " + parts[idx]);
}
} Type guard
boolean isNumberLInDayOfWeekOnly(String[] parts) {
for (int idx = 0; idx < parts.length; idx++) {
if (parts[idx].matches(".*\\d+L.*") && idx != 5) return false;
}
return true; Try / catch
try {
new CronExpression(cron);
} catch (ParseException e) {
if (e.getMessage().contains("'L' option is not valid here")) {
// use standalone 'L' for day-of-month, or move 'NL' to day-of-week
}
} Prevention
- Use standalone 'L' for 'last day of month' in day-of-month, not 'NL'.
- Reserve 'NL' for day-of-week 'last occurrence of weekday N'.
- Document the distinction between day-of-month 'L' and day-of-week 'NL'.
When it happens
Trigger: A cron expression like '0 0 0 5L * ?' where '5L' appears in the day-of-month field. The parser reads 5 as a digit, enters checkNext, finds 'L', and since type != DAY_OF_WEEK, throws at line 759.
Common situations: Confusing the day-of-month 'L' (which stands alone as just 'L' or 'L-N') with the day-of-week 'NL' pattern. A developer writes '15L' expecting 'last day' but the parser interprets the digit first.
Related errors
- '?' can only be specified for Day-of-Month -OR- Day-of-Week.
- '/' must be followed by an integer.
- Unexpected end of string.
- 'L' not expected in seconds, minutes or hours fields.
- Offset from last day must be <= {}
AI-assisted analysis of xuxueli/xxl-job@e74c784f68 (2026-08-14).
Data as JSON: /api/errors/d096f66ddbb376c2.
Report an issue: GitHub.