{"record":{"id":"c0a73b61f436beda","repo":"jeecgboot/JeecgBoot","slug":"can-only-be-specified-for-day-of-month-or-da","errorCode":null,"errorMessage":"'?' can only be specified for Day-of-Month -OR- Day-of-Week.","messagePattern":"'\\?' can only be specified for Day-of-Month -OR- Day-of-Week\\.","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":626,"sourceCode":"            addToSet(sval, eval, incr, type);\n            return (i + 3);\n        }\n\n        if (c == '?') {\n            i++;\n            if ((i + 1) < s.length()\n                    && (s.charAt(i) != ' ' && s.charAt(i + 1) != '\\t')) {\n                throw new ParseException(\"Illegal character after '?': \"\n                        + s.charAt(i), i);\n            }\n            if (type != DAY_OF_WEEK && type != DAY_OF_MONTH) {\n                throw new ParseException(\n                        \"'?' can only be specified for Day-of-Month or Day-of-Week.\",\n                        i);\n            }\n            if (type == DAY_OF_WEEK) {\n                if (!daysOfMonth.isEmpty() && daysOfMonth.last() == NO_SPEC_INT) {\n                    throw new ParseException(\n                            \"'?' can only be specified for Day-of-Month -OR- Day-of-Week.\",\n                            i);\n                }\n            }\n\n            addToSet(NO_SPEC_INT, -1, 0, type);\n            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 == '*') {","sourceCodeStart":608,"sourceCodeEnd":644,"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#L608-L644","documentation":"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.","triggerScenarios":"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.","commonSituations":"Copy/paste of '?' into both day fields; templates defaulting both to '?'; misunderstanding that one day field must carry the actual schedule.","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='?')."],"exampleFix":"// before - both day fields are '?'\nString cron = \"0 0 0 ? * ?\";  // Illegal: ? in both day fields\n\n// after - dom specified, dow unspecified\nString cron = \"0 0 0 * * ?\";","handlingStrategy":"validation","validationCode":"// Exactly one of dom/dow may be '?'; the other must be a concrete spec.\npublic static String validateDayFieldExclusion(String expr) {\n    String[] f = expr.trim().split(\"\\\\s+\");\n    if (f.length < 6) return null; // let field-count check handle it\n    boolean domQ = f[3].trim().equals(\"?\");\n    boolean dowQ = f[5].trim().equals(\"?\");\n    if (domQ && dowQ) return \"Only one of day-of-month / day-of-week may be '?', not both.\";\n    return null;\n}","typeGuard":"public static boolean exactlyOneDayFieldIsQuestionMark(String[] fields) {\n    if (fields.length < 6) return false;\n    boolean domQ = fields[3].trim().equals(\"?\");\n    boolean dowQ = fields[5].trim().equals(\"?\");\n    return domQ ^ dowQ;\n}","tryCatchPattern":"try {\n    new CronExpression(expr);\n} catch (ParseException e) {\n    if (e.getMessage().contains(\"-OR-\")) {\n        return \"Set one day field to '?' and give the other a value (e.g. '* * * * * ?'). \" + e.getMessage();\n    }\n    throw e;\n}","preventionTips":["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."],"tags":["cron","xxl-job","day-field","wildcard","validation","parse-error"],"backgroundTag":null,"analyzedSha":"96fb33f5ec68516da0b0147da06b2eb0419e063a","analyzedAt":"2026-08-14T00:04:16.786Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}