Skip to content

Commit 01a4987

Browse files
authored
[BUG]Invalid cron date schedule creates infinite loop in flytescheduler (#3312)
Signed-off-by: Alex Wu <c.alexwu@gmail.com>
1 parent e6e257e commit 01a4987

File tree

2 files changed

+38
-6
lines changed

2 files changed

+38
-6
lines changed

flytekit/core/schedule.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -142,12 +142,12 @@ def _validate_expression(cron_expression: str):
142142
def _validate_schedule(schedule: str):
143143
if schedule.lower() not in CronSchedule._VALID_CRON_ALIASES:
144144
try:
145-
croniter.croniter(schedule)
146-
except Exception:
147-
raise ValueError(
148-
"Schedule is invalid. It must be set to either a cron alias or valid cron expression."
149-
f" Provided schedule: {schedule}"
150-
)
145+
# Validate the cron expression
146+
cron = croniter.croniter(schedule)
147+
# Try to get the next occurrence to validate the schedule
148+
cron.get_next(datetime.datetime)
149+
except Exception as e:
150+
raise ValueError(f"Schedule is invalid. Provided schedule: {schedule} Error: {str(e)}")
151151

152152
@staticmethod
153153
def _validate_offset(offset: str):

tests/flytekit/unit/core/test_schedule.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,3 +153,35 @@ def quadruple(a: int) -> int:
153153
assert lp.schedule == _schedule_models.Schedule(
154154
"kickoff_input", rate=_schedule_models.Schedule.FixedRate(12, _schedule_models.Schedule.FixedRateUnit.HOUR)
155155
)
156+
157+
158+
@pytest.mark.parametrize(
159+
"invalid_schedule",
160+
[
161+
"0 0 31 2 *", # February 31st (does not exist)
162+
"0 0 30 2 *", # February 30th (does not exist)
163+
"0 0 31 4 *", # April 31st (does not exist)
164+
"0 0 31 6 *", # June 31st (does not exist)
165+
],
166+
)
167+
def test_cron_invalid_date_combinations(invalid_schedule):
168+
"""Test that CronSchedule rejects invalid date combinations like 31st of February."""
169+
with pytest.raises(ValueError, match="Schedule is invalid."):
170+
CronSchedule(schedule=invalid_schedule)
171+
172+
173+
@pytest.mark.parametrize(
174+
"valid_schedule",
175+
[
176+
"0 0 28 2 *", # February 28th (always valid)
177+
"0 0 29 2 *", # February 29th (valid in leap years - handled by croniter)
178+
"0 0 30 4 *", # April 30th (valid)
179+
"0 0 31 1 *", # January 31st (valid)
180+
"0 0 31 3 *", # March 31st (valid)
181+
],
182+
)
183+
def test_cron_valid_date_combinations(valid_schedule):
184+
"""Test that CronSchedule accepts valid date combinations."""
185+
# These should not raise any exceptions
186+
obj = CronSchedule(schedule=valid_schedule)
187+
assert obj.cron_schedule.schedule == valid_schedule

0 commit comments

Comments
 (0)