{"record":{"id":"5d013c88c8e24be1","repo":"alibaba/spring-cloud-alibaba","slug":"offset-from-last-day-must-be-30","errorCode":null,"errorMessage":"Offset from last day must be <= 30","messagePattern":"Offset from last day must be <= 30","errorType":"exception","errorClass":"ParseException","httpStatus":null,"severity":"error","filePath":"spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-schedulerx/src/main/java/com/alibaba/cloud/scheduling/schedulerx/util/CronExpression.java","lineNumber":472,"sourceCode":"\n\t\t\taddToSet(ALL_SPEC_INT, -1, incr, type);\n\t\t\treturn i;\n\t\t}\n\t\telse if (c == 'L') {\n\t\t\ti++;\n\t\t\tif (type == DAY_OF_MONTH) {\n\t\t\t\tlastdayOfMonth = true;\n\t\t\t}\n\t\t\tif (type == DAY_OF_WEEK) {\n\t\t\t\taddToSet(7, 7, 0, type);\n\t\t\t}\n\t\t\tif (type == DAY_OF_MONTH && s.length() > i) {\n\t\t\t\tc = s.charAt(i);\n\t\t\t\tif (c == '-') {\n\t\t\t\t\tfinal ValueSet vs = getValue(0, s, i + 1);\n\t\t\t\t\tlastdayOffset = vs.value;\n\t\t\t\t\tif (lastdayOffset > 30) {\n\t\t\t\t\t\tthrow new ParseException(\"Offset from last day must be <= 30\", i + 1);\n\t\t\t\t\t}\n\t\t\t\t\ti = vs.pos;\n\t\t\t\t}\n\t\t\t\tif (s.length() > i) {\n\t\t\t\t\tc = s.charAt(i);\n\t\t\t\t\tif (c == 'W') {\n\t\t\t\t\t\tnearestWeekday = true;\n\t\t\t\t\t\ti++;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn i;\n\t\t}\n\t\telse if (c >= '0' && c <= '9') {\n\t\t\tint val = Integer.parseInt(String.valueOf(c));\n\t\t\ti++;\n\t\t\tif (i >= s.length()) {\n\t\t\t\taddToSet(val, -1, -1, type);","sourceCodeStart":454,"sourceCodeEnd":490,"githubUrl":"https://github.com/alibaba/spring-cloud-alibaba/blob/115d5901102009492e05d5ec18c3f79cad4077d0/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-schedulerx/src/main/java/com/alibaba/cloud/scheduling/schedulerx/util/CronExpression.java#L454-L490","documentation":"Thrown when the 'L' (last day) construct in the DAY_OF_MONTH field is given an offset larger than 30 via 'L-N' (guard at CronExpression.java:471-472). 'L-N' means 'N days before the last day of the month', so an offset beyond 30 is meaningless.","triggerScenarios":"`new CronExpression(\"0 0 0 L-31 * ?\")` (last-day offset 31). Any 'L-NN' in the day-of-month field where NN > 30.","commonSituations":"Developer misreads 'L-N' as 'last N days' or typos the offset; also from concatenating an unbounded value into the offset.","solutions":["Use an offset <= 30, e.g. 'L-31' -> 'L-5' for the 5th-to-last day.","If you need a range of final days, enumerate them or use a step, not a large 'L-N'.","Sanitize any templated offset value before injecting into the cron string."],"exampleFix":"// before\nnew CronExpression(\"0 0 0 L-31 * ?\");\n// after\nnew CronExpression(\"0 0 0 L-5 * ?\"); // 5 days before month end","handlingStrategy":"validation","validationCode":"// 'L-N' offset must be 0..30.\nstatic boolean lastDayOffsetOk(String domField) {\n    if (!domField.startsWith(\"L-\")) return true;\n    try {\n        int off = Integer.parseInt(domField.substring(2));\n        return off >= 0 && off <= 30;\n    } catch (NumberFormatException e) { return false; }\n}","typeGuard":null,"tryCatchPattern":"try {\n    CronExpression cron = new CronExpression(raw);\n} catch (ParseException e) {\n    throw new IllegalArgumentException(\"Invalid cron expression: \" + raw, e);\n}","preventionTips":["Cap 'L-N' offsets at 30.","Sanitize templated offset values before injection.","Prefer 'L' for the last day rather than guessing offsets."],"tags":["cron","schedulerx","spring-cloud-alibaba","configuration","parsing","validation","range-check"],"backgroundTag":null,"analyzedSha":"115d5901102009492e05d5ec18c3f79cad4077d0","analyzedAt":"2026-08-14T04:47:13.900Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}