flowable/flowable-engine · error · ParseException
Illegal character after ?:
Error message
Illegal character after ?:
What it means
Thrown when '?' is followed by a character that is not a space or tab: the parser requires '?' to be the last token of its field, terminated by whitespace or end-of-string. The check tests that the next character is ' ' and the one after is '\t' (an odd compound condition), and rejects anything else. '?' is only meaningful as a placeholder meaning 'no specific value' in Day-of-Month or Day-of-Week.
Source
Thrown at modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/calendar/CronExpression.java:615
lastdayOfWeek = true;
i++;
}
}
} else {
throw new ParseException("Illegal characters for this position: '" + sub + "'", i);
}
if (eval != -1) {
incr = 1;
}
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 && !lastdayOfMonth) {
int val = daysOfMonth.last();
if (val == 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);View on GitHub (pinned to d6d39ce1c6)
Solutions
- Ensure '?' is followed by a space or is at the end of the string: '0 0 12 ? * MON' not '0 0 12 ?* MON'.
- When building expressions dynamically, always join fields with explicit ' ' separators.
- Replace '?' with '*' if you intended 'every value' rather than 'no specific value'.
- Validate with CronExpression.validateExpression before saving user-supplied schedules.
Example fix
// before
new CronExpression("0 0 12 ?* MON");
// after
new CronExpression("0 0 12 ? * MON"); Defensive patterns
Strategy: validation
Validate before calling
if (expr.contains("?") && java.util.regex.Pattern.compile("\\?[^\\s]").matcher(expr).find()) {
throw new IllegalArgumentException("'?' must be followed by whitespace or end of expression: " + expr);
} Try / catch
try {
new CronExpression(expr);
} catch (java.text.ParseException e) {
if (e.getMessage().contains("Illegal character after '?'")) {
log.error("Missing space after '?' in cron: {}", expr);
}
} Prevention
- Always separate fields with explicit spaces when building expressions dynamically
- Keep '?' as the last token of its field
- Normalize whitespace in stored cron strings
When it happens
Trigger: new CronExpression("0 0 12 ?* * ...") — '?' immediately followed by more characters ('?*', '?L', '?15'); expression strings with missing spaces after '?'.
Common situations: Missing whitespace when concatenating fields programmatically (e.g. "... ? " + dow with dow empty or glued); template strings where a separator was dropped; hand-editing an expression and deleting the space after '?'.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- Illegal characters for this position: '
- Invalid Day-of-Week value: '
- A numeric value between 1 and 5 must follow the '#' option
- '?' can only be specified for Day-of-Month or Day-of-Week.
- '?' can only be specified for Day-of-Month -OR- Day-of-Week.
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/1aba1eb5d3675511.
Report an issue: GitHub.