{"record":{"id":"9cf27e14e7544ff0","repo":"jeecgboot/JeecgBoot","slug":"unexpected-end-of-string","errorCode":null,"errorMessage":"Unexpected end of string.","messagePattern":"Unexpected end of string\\.","errorType":"exception","errorClass":"ParseException","httpStatus":null,"severity":"error","filePath":"jeecg-boot/jeecg-server-cloud/jeecg-visual/jeecg-cloud-xxljob/src/main/java/com/xxl/job/admin/business/scheduler/cron/CronExpression.java","lineNumber":651,"sourceCode":"            return i;\n        }\n\n        if (c == '*' || c == '/') {\n            if (c == '*' && (i + 1) >= s.length()) {\n                addToSet(ALL_SPEC_INT, -1, incr, type);\n                return i + 1;\n            } else if (c == '/'\n                    && ((i + 1) >= s.length() || s.charAt(i + 1) == ' ' || s\n                    .charAt(i + 1) == '\\t')) {\n                throw new ParseException(\"'/' must be followed by an integer.\", i);\n            } else if (c == '*') {\n                i++;\n            }\n            c = s.charAt(i);\n            if (c == '/') { // is an increment specified?\n                i++;\n                if (i >= s.length()) {\n                    throw new ParseException(\"Unexpected end of string.\", i);\n                }\n\n                incr = getNumericValue(s, i);\n\n                i++;\n                if (incr > 10) {\n                    i++;\n                }\n                checkIncrementRange(incr, type, i);\n            } else {\n                incr = 1;\n            }\n\n            addToSet(ALL_SPEC_INT, -1, incr, type);\n            return i;\n        } else if (c == 'L') {\n\n            if(type < DAY_OF_MONTH)","sourceCodeStart":633,"sourceCodeEnd":669,"githubUrl":"https://github.com/jeecgboot/JeecgBoot/blob/96fb33f5ec68516da0b0147da06b2eb0419e063a/jeecg-boot/jeecg-server-cloud/jeecg-visual/jeecg-cloud-xxljob/src/main/java/com/xxl/job/admin/business/scheduler/cron/CronExpression.java#L633-L669","documentation":"After consuming a '/' in the '*' or '/' handling branch, the parser does i++ and then checks i >= s.length(). If the increment was at the very end of the string and there is no character left to read, this is thrown. It differs from 290 in that this is the inner re-check inside the increment block, after the '/' was already accepted as a valid operator at this position.","triggerScenarios":"A '*' or value followed by '/' that lands exactly at the end of input with nothing after the i++ advance, e.g. '0 0 0 * * ?/' in a trailing year field, or an expression that ends right after the slash in the field being parsed. Because this is per-field, it fires when the field's substring ends at '/'.","commonSituations":"Truncated expressions where the final increment digit was chopped off; concatenation that left a '/' at the boundary; year-field appended with an empty step.","solutions":["Add the missing integer after the trailing '/'.","Trim the expression and re-validate field by field to find which field ends in '/'.","If a step is optional, only append '/step' when the step value is present and > 0."],"exampleFix":"// before - expression ends right after '/'\nString cron = \"0 0 0 * * ? 2025/\";  // Unexpected end of string\n\n// after - provide the year step\nString cron = \"0 0 0 * * ? 2025/2\";","handlingStrategy":"validation","validationCode":"// No field may end with '/' (the increment must follow in the same field).\npublic static String validateNoTrailingSlash(String expr) {\n    for (String field : expr.trim().split(\"\\\\s+\")) {\n        if (field.endsWith(\"/\")) return \"field ends with '/' and is missing its increment: \" + field;\n    }\n    return null;\n}","typeGuard":"public static boolean noFieldEndsWithSlash(String expr) {\n    if (expr == null) return true;\n    for (String field : expr.trim().split(\"\\\\s+\")) {\n        if (field.endsWith(\"/\")) return false;\n    }\n    return true;\n}","tryCatchPattern":"try {\n    new CronExpression(expr);\n} catch (ParseException e) {\n    if (e.getMessage().startsWith(\"Unexpected end of string\")) {\n        return \"The expression is truncated after a '/'; supply the increment. \" + e.getMessage();\n    }\n    throw e;\n}","preventionTips":["Strip trailing whitespace and ensure no field ends with '/'.","When concatenating fields programmatically, verify each step value is present.","Unit-test generated expressions end-to-end before saving them."],"tags":["cron","xxl-job","step-operator","validation","parse-error"],"backgroundTag":null,"analyzedSha":"96fb33f5ec68516da0b0147da06b2eb0419e063a","analyzedAt":"2026-08-14T00:04:16.786Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}