{"record":{"id":"59f782e7be4ef82f","repo":"apache/superset","slug":"invalid-crontab-schedule-cron-schedule-s-never","errorCode":null,"errorMessage":"Invalid crontab schedule: %(cron_schedule)s never matches a valid date","messagePattern":"Invalid crontab schedule: (.+?) never matches a valid date","errorType":"validation","errorClass":"ReportScheduleCrontabNotValidError","httpStatus":422,"severity":"error","filePath":"superset/commands/report/base.py","lineNumber":360,"sourceCode":"        # Since configuration is in minutes, we only need to validate\n        # in case `minimum_interval` is <= 120 (2min)\n        if minimum_interval < 120:\n            return\n\n        iterations = 60 if minimum_interval <= 3660 else 24\n        try:\n            schedule = croniter(cron_schedule)\n            current_exec = next(schedule)\n\n            for _i in range(iterations):\n                next_exec = next(schedule)\n                diff, current_exec = next_exec - current_exec, next_exec\n                if int(diff) < minimum_interval:\n                    raise ReportScheduleFrequencyNotAllowed(\n                        report_type=report_type, minimum_interval=minimum_interval\n                    )\n        except CroniterBadDateError as ex:\n            raise ReportScheduleCrontabNotValidError(\n                cron_schedule=cron_schedule\n            ) from ex\n","sourceCodeStart":342,"sourceCodeEnd":363,"githubUrl":"https://github.com/apache/superset/blob/f4587218dd19d046c3e4d00063e7d27f8a2ed354/superset/commands/report/base.py#L342-L363","documentation":"Raised when croniter(cron_schedule) raises CroniterBadDateError, meaning the crontab expression can never match a valid date. Superset wraps it as ReportScheduleCrontabNotValidError so the API returns an actionable message naming the offending crontab instead of a raw croniter traceback. It is part of crontab validation on report/alert create and update.","triggerScenarios":"Submitting a crontab such as '0 0 31 2 *' (Feb 31) or '0 0 30 2 *' (Feb 30) — dates that never exist — to the report/alert API. Any five-field expression whose day-of-month/month combination is impossible.","commonSituations":"Hand-written crontabs copied from a spec ('run on the 31st of February'); UI or script generating end-of-month schedules without clamping; typos in the month field.","solutions":["Fix the impossible date: use a real combination like '0 0 31 1,3,5,7,8,10,12 *' (31st only in 31-day months) or '59 23 L * *' style last-day syntax if supported","Pre-validate the expression with croniter before calling the API","If you intended 'last day of month', use a day-of-week/last-day form croniter accepts"],"exampleFix":"# before\ncrontab = '0 0 31 2 *'   # Feb 31 never exists -> CroniterBadDateError\n\n# after\nfrom croniter import croniter\ncroniter.is_valid('0 0 28 2 *')  # True; use a real date","handlingStrategy":"validation","validationCode":"from croniter import croniter\n\nif not croniter.is_valid(crontab):\n    raise ValueError(f'Invalid crontab: {crontab!r}')  # catches never-matching dates","typeGuard":"def is_valid_crontab(expr: str) -> bool:\n    return croniter.is_valid(expr)","tryCatchPattern":"try:\n    CreateReportCommand(user, payload).run()\nexcept ReportScheduleCrontabNotValidError as ex:\n    show_error(f'{ex.cron_schedule} never matches a valid date; fix the expression')","preventionTips":["Always run croniter.is_valid on user-supplied or generated crontabs before persisting","Avoid day-of-month 29-31 combined with months that lack those days","Prefer generated schedules from a calendar library over hand-typed dates"],"tags":["scheduling","cron","validation","alerts-reports"],"backgroundTag":null,"analyzedSha":"f4587218dd19d046c3e4d00063e7d27f8a2ed354","analyzedAt":"2026-08-14T22:39:27.425Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}