xuxueli/xxl-job · error · ParseException

Month values must be between 1 and 12

Error message

Month values must be between 1 and 12

What it means

Thrown by CronExpression's field-validation routine (addToSet) when the MONTH token holds a value below 1 or above 12, or a range whose upper bound exceeds 12. Months are 1-based (1=Jan ... 12=Dec). Only the '*' wildcard (ALL_SPEC_INT=99) is exempt; '?' is not valid for the month field.

Source

Thrown at xxl-job-admin/src/main/java/com/xxl/job/admin/business/scheduler/cron/CronExpression.java:1005

            if ((val < 0 || val > 59 || end > 59) && (val != ALL_SPEC_INT)) {
                throw new ParseException(
                        "Minute and Second values must be between 0 and 59",
                        -1);
            }
        } else if (type == HOUR) {
            if ((val < 0 || val > 23 || end > 23) && (val != ALL_SPEC_INT)) {
                throw new ParseException(
                        "Hour values must be between 0 and 23", -1);
            }
        } else if (type == DAY_OF_MONTH) {
            if ((val < 1 || val > 31 || end > 31) && (val != ALL_SPEC_INT)
                    && (val != NO_SPEC_INT)) {
                throw new ParseException(
                        "Day of month values must be between 1 and 31", -1);
            }
        } else if (type == MONTH) {
            if ((val < 1 || val > 12 || end > 12) && (val != ALL_SPEC_INT)) {
                throw new ParseException(
                        "Month values must be between 1 and 12", -1);
            }
        } else if (type == DAY_OF_WEEK) {
            if ((val == 0 || val > 7 || end > 7) && (val != ALL_SPEC_INT)
                    && (val != NO_SPEC_INT)) {
                throw new ParseException(
                        "Day-of-Week values must be between 1 and 7", -1);
            }
        }

        if ((incr == 0 || incr == -1) && val != ALL_SPEC_INT) {
            if (val != -1) {
                set.add(val);
            } else {
                set.add(NO_SPEC);
            }

            return;

View on GitHub (pinned to e74c784f68)

Solutions

  1. Keep month literals in 1-12 (January=1, December=12).
  2. Use '*' for every month; do not use '?' here (month has no NO_SPEC exemption).
  3. Add a parsing test for generated cron strings before persisting.

Example fix

// before
new CronExpression("0 0 12 * 13 ?"); // month 13 invalid
// after
new CronExpression("0 0 12 * 12 ?"); // December = 12
Defensive patterns

Strategy: validation

Validate before calling

// Guard month literals 1-12 before parsing
String[] f = expr.trim().split("\\s+");
String mon = (f.length >= 6) ? f[4] : f[3];
if (mon.matches("\\d+")) {
    int m = Integer.parseInt(mon);
    if (m < 1 || m > 12) throw new IllegalArgumentException("month out of range: " + m);
}

Try / catch

try {
    CronExpression c = new CronExpression(expr);
} catch (java.text.ParseException e) {
    log.warn("Invalid cron '{}': {}", expr, e.getMessage());
}

Prevention

When it happens

Trigger: Constructing CronExpression with a month like "13", "0", or a range "1-14". Occurs during expression parsing/construction.

Common situations: Using 0 to mean January (it is 1 here); off-by-one from a zero-indexed month list; copy-pasting schedules from systems with different month encoding.

Related errors


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