xuxueli/xxl-job · 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

The 'L' (last) modifier is only valid in the Day-of-Month and Day-of-Week fields. It means 'last day of the month' or 'last weekday of the month'. This error fires when 'L' appears in the seconds, minutes, or hours field (type < DAY_OF_MONTH, i.e., type 0, 1, or 2).

Source

Thrown at xxl-job-admin/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 e74c784f68)

Solutions

  1. Remove the 'L' from the seconds/minutes/hours field; use a plain number or '*'.
  2. Move the 'L' logic to the day-of-month or day-of-week field where it is valid (e.g., '0 0 0 L * ?').
  3. Review the Quartz cron field reference to confirm where 'L' is permitted.

Example fix

// before
"0 0 8L * * ?"
// after
"0 8 0 L * ?"
Defensive patterns

Strategy: validation

Validate before calling

// 'L' may only appear in field index 3 (day-of-month) or 5 (day-of-week)
String[] parts = cron.trim().split("\\s+");
for (int idx = 0; idx < parts.length; idx++) {
    if (parts[idx].contains("L") && idx != 3 && idx != 5) {
        throw new IllegalArgumentException("'L' is not valid in field " + idx + ": " + parts[idx]);
    }
}

Type guard

boolean isLInValidField(String[] parts) {
    return (parts[0] == null || !parts[0].contains("L"))
        && (parts[1] == null || !parts[1].contains("L"))
        && (parts.length < 3 || !parts[2].contains("L"));

Try / catch

try {
    new CronExpression(cron);
} catch (ParseException e) {
    if (e.getMessage().contains("'L' not expected")) {
        // move 'L' to day-of-month or day-of-week
    }
}

Prevention

When it happens

Trigger: A cron expression such as '0 0 8L * * ?' where 'L' appears in the hour field, or '0 0L * * * ?' in the minute field. The check at line 669 tests type < DAY_OF_MONTH (3).

Common situations: Misunderstanding the scope of the 'L' modifier, or a cron-generation tool that applies 'L' to the wrong field index. Developers familiar with non-Quartz cron may not expect 'L' to be field-restricted.

Related errors


AI-assisted analysis of xuxueli/xxl-job@e74c784f68 (2026-08-14). Data as JSON: /api/errors/e2b4f000d2ff11e6. Report an issue: GitHub.