jeecgboot/JeecgBoot · error · ParseException
'?' can only be specified for Day-of-Month -OR- Day-of-Week.
Error message
'?' can only be specified for Day-of-Month -OR- Day-of-Week.
What it means
Stricter day-field rule: if '?' is being set in DAY_OF_WEEK but daysOfMonth already contains NO_SPEC_INT (i.e. dom was also '?'), both day fields would be unspecified, which Quartz forbids. Exactly one of dom/dow must be '?' and the other must be a real spec. This guards against '* * * ? * ?' style inputs.
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:626
addToSet(sval, eval, incr, type);
return (i + 3);
}
if (c == '?') {
i++;
if ((i + 1) < s.length()
&& (s.charAt(i) != ' ' && s.charAt(i + 1) != '\t')) {
throw new ParseException("Illegal character after '?': "
+ s.charAt(i), i);
}
if (type != DAY_OF_WEEK && type != DAY_OF_MONTH) {
throw new ParseException(
"'?' can only be specified for Day-of-Month or Day-of-Week.",
i);
}
if (type == DAY_OF_WEEK) {
if (!daysOfMonth.isEmpty() && daysOfMonth.last() == NO_SPEC_INT) {
throw new ParseException(
"'?' can only be specified for Day-of-Month -OR- Day-of-Week.",
i);
}
}
addToSet(NO_SPEC_INT, -1, 0, type);
return i;
}
if (c == '*' || c == '/') {
if (c == '*' && (i + 1) >= s.length()) {
addToSet(ALL_SPEC_INT, -1, incr, type);
return i + 1;
} else if (c == '/'
&& ((i + 1) >= s.length() || s.charAt(i + 1) == ' ' || s
.charAt(i + 1) == '\t')) {
throw new ParseException("'/' must be followed by an integer.", i);
} else if (c == '*') {View on GitHub (pinned to 96fb33f5ec)
Solutions
- Set exactly one of day-of-month / day-of-week to '?' and give the other a concrete value ('*', '15', 'MON', etc.).
- A common safe pair is dom='?' with dow specified, or dom='*' / a number with dow='?'.
- If you want 'every day', use '0 0 0 * * ?' (dom='*', dow='?').
Example fix
// before - both day fields are '?' String cron = "0 0 0 ? * ?"; // Illegal: ? in both day fields // after - dom specified, dow unspecified String cron = "0 0 0 * * ?";
Defensive patterns
Strategy: validation
Validate before calling
// Exactly one of dom/dow may be '?'; the other must be a concrete spec.
public static String validateDayFieldExclusion(String expr) {
String[] f = expr.trim().split("\\s+");
if (f.length < 6) return null; // let field-count check handle it
boolean domQ = f[3].trim().equals("?");
boolean dowQ = f[5].trim().equals("?");
if (domQ && dowQ) return "Only one of day-of-month / day-of-week may be '?', not both.";
return null;
} Type guard
public static boolean exactlyOneDayFieldIsQuestionMark(String[] fields) {
if (fields.length < 6) return false;
boolean domQ = fields[3].trim().equals("?");
boolean dowQ = fields[5].trim().equals("?");
return domQ ^ dowQ;
} Try / catch
try {
new CronExpression(expr);
} catch (ParseException e) {
if (e.getMessage().contains("-OR-")) {
return "Set one day field to '?' and give the other a value (e.g. '* * * * * ?'). " + e.getMessage();
}
throw e;
} Prevention
- Default to dom='*' + dow='?' for daily schedules and dom='?' + dow=<value> for weekly.
- Reject templates that set both day fields to '?'.
- Validate the dom/dow exclusion rule before persisting the job.
When it happens
Trigger: Both day fields set to '?': '0 0 0 ? * ?' (dom='?' and dow='?'). The check reads daysOfMonth.last() == NO_SPEC_INT after dom was parsed as '?'. Also fires when an earlier '?' in dom is followed by a later '?' in dow during the same parse.
Common situations: Copy/paste of '?' into both day fields; templates defaulting both to '?'; misunderstanding that one day field must carry the actual schedule.
Related errors
- Illegal character after '?': {s.charAt(i)}
- Illegal cron expression format ({e})
- Invalid Month value: '{sub}'
- Invalid Day-of-Week value: '{sub}'
- A numeric value between 1 and 5 must follow the '#' option
AI-assisted analysis of jeecgboot/JeecgBoot@96fb33f5ec (2026-08-14).
Data as JSON: /api/errors/c0a73b61f436beda.
Report an issue: GitHub.