Skip to content

Do not crash when an unsatisfiable day-of-month is OR-combined with day-of-week#243

Open
semx wants to merge 1 commit into
pallets-eco:mainfrom
semx:fix-dom-dow-unsatisfiable
Open

Do not crash when an unsatisfiable day-of-month is OR-combined with day-of-week#243
semx wants to merge 1 commit into
pallets-eco:mainfrom
semx:fix-dom-dow-unsatisfiable

Conversation

@semx

@semx semx commented Jul 15, 2026

Copy link
Copy Markdown

Problem

When the day-of-month and day-of-week fields are both restricted (neither is *) with the default OR semantics (day_or=True, matching Vixie/POSIX cron), croniter computes the day-of-month next time and the day-of-week next time and returns the earlier of the two.

If the day-of-month side is unsatisfiable — e.g. day 31 restricted to months that never have a 31st — the internal _calc raises CroniterBadDateError, which propagates and aborts get_next/get_prev entirely, discarding the perfectly valid day-of-week result.

Under OR semantics an impossible side should contribute nothing, not kill the schedule. is_valid() already accepts these expressions, so they pass validation and then crash at resolution time.

from datetime import datetime
from croniter import croniter

# 06:30 on (Feb 31 — impossible) OR (any Monday in February)
croniter.is_valid("30 6 31 2 1")                         # True
croniter("30 6 31 2 1", datetime(2021, 1, 1)).get_next(datetime)
# before: CroniterBadDateError: failed to find next date
# after:  2021-02-01 06:30:00   (first Monday of February)

man 5 crontab (Vixie): "if both fields are restricted (i.e., aren't *), the command will be run when either field matches the current time." Day 31 never matches in February, so the schedule should fire on Mondays.

Other affected expressions: 0 0 30 2 1-5, 0 0 31 4 0, 0 0 31 2,4,6,9,11 1. Any scheduler built on croniter (Airflow, Dagster, Celery-beat, APScheduler, …) errors at schedule-resolution time for these, and the intended weekday schedule never runs.

Fix

Guard both branches of the DOM/DOW union: if a side raises CroniterBadDateError, treat it as "no match" and use the other side; raise only if both are unsatisfiable. Satisfiable cases are unchanged.

Tests

Added test_dom_dow_both_restricted_with_unsatisfiable_dom covering get_next, get_prev, and that a satisfiable DOM side still behaves as before. Full suite: 221 passed.

@semx semx closed this Jul 15, 2026
@semx
semx force-pushed the fix-dom-dow-unsatisfiable branch from 252490a to 28ec077 Compare July 15, 2026 09:33
@semx semx reopened this Jul 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant