alibaba/spring-cloud-alibaba · error · ParseException
Unexpected end of string.
Error message
Unexpected end of string.
What it means
Thrown after a '*' (or leading '/') is consumed and a following '/' is found, but advancing past the '/' runs off the end of the token — i.e. there is no increment value after the slash at all (guard at CronExpression.java:425-426). This differs from error 80, which fires on a leading '/' followed by whitespace; error 81 fires when '*' was already consumed and the trailing '/' has no digit. It is a checked ParseException from the constructor.
Source
Thrown at spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-schedulerx/src/main/java/com/alibaba/cloud/scheduling/schedulerx/util/CronExpression.java:426
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 == '*') {
i++;
}
c = s.charAt(i);
if (c == '/') { // is an increment specified?
i++;
if (i >= s.length()) {
throw new ParseException("Unexpected end of string.", i);
}
incr = getNumericValue(s, i);
i++;
if (incr > 10) {
i++;
}
if (incr > 59 && (type == SECOND || type == MINUTE)) {
throw new ParseException("Increment > 60 : " + incr, i);
}
else if (incr > 23 && (type == HOUR)) {
throw new ParseException("Increment > 24 : " + incr, i);
}
else if (incr > 31 && (type == DAY_OF_MONTH)) {
throw new ParseException("Increment > 31 : " + incr, i);
}
else if (incr > 7 && (type == DAY_OF_WEEK)) {View on GitHub (pinned to 115d590110)
Solutions
- Add the missing step integer after the trailing '/', e.g. '*/' -> '*/2'.
- Audit config templates/property placeholders so an empty `${step}` cannot yield a bare '*/'.
- Use '*' alone if you want 'every' rather than a step.
- Validate the cron string with the helper below before constructing.
Example fix
// before
new CronExpression("0 0 0 ? * */");
// after
new CronExpression("0 0 0 ? * */2"); // every 2 days-of-week Defensive patterns
Strategy: try-catch
Validate before calling
// Reject tokens that end with a bare '*/'.
static boolean noTrailingBareSlash(String cron) {
if (cron == null) return true;
for (String f : cron.trim().split("\\s+")) {
if (f.endsWith("*/") || f.endsWith("/")) return false;
}
return true;
} Try / catch
try {
CronExpression cron = new CronExpression(raw);
} catch (ParseException e) {
throw new IllegalArgumentException("Invalid cron expression: " + raw, e);
} Prevention
- Never leave '*/' without a following digit.
- Default empty step placeholders to a concrete value (e.g. ${step:1}).
- Unit-test templated cron strings for all placeholder combinations.
When it happens
Trigger: Constructing `new CronExpression("0 0 0 ? * */")` where the day-of-week (last) field is '*/' with nothing after the slash. Any field token ending in exactly '*/' with no digits triggers the `i >= s.length()` check at line 425.
Common situations: Truncation of a cron string (e.g. a config value trimmed to `"0 0 0 ? * */"`), or a templated cron where `${step}` was empty producing `*/`. Also from copy-paste where the trailing increment was dropped.
Related errors
- '/' must be followed by an integer.
- Increment > 60 : {}
- Increment > 24 : {}
- Increment > 31 : {}
- Increment > 7 : {}
AI-assisted analysis of alibaba/spring-cloud-alibaba@115d590110 (2026-08-14).
Data as JSON: /api/errors/e66497feae3dea32.
Report an issue: GitHub.