HKUDS/DeepTutor · error · ValueError
cron expressions need the 'croniter' package — use an 'every
Error message
cron expressions need the 'croniter' package — use an 'every' or 'at' schedule instead
What it means
compute_next_run catches ImportError from importing croniter and re-raises as ValueError, telling you cron expressions are unsupported without the optional dependency. Only 'cron' kind schedules need croniter; 'every' and 'at' schedules use builtin logic.
Source
Thrown at deeptutor/services/cron/service.py:141
return None
if schedule.kind == "every":
if not schedule.every_seconds or schedule.every_seconds <= 0:
return None
return now_ms + schedule.every_seconds * 1000
if schedule.kind == "cron" and schedule.expr:
try:
from zoneinfo import ZoneInfo
from croniter import croniter
tz = ZoneInfo(schedule.tz) if schedule.tz else datetime.now().astimezone().tzinfo
base = datetime.fromtimestamp(now_ms / 1000, tz=tz)
next_dt = croniter(schedule.expr, base).get_next(datetime)
return int(next_dt.timestamp() * 1000)
except ImportError:
raise ValueError(
"cron expressions need the 'croniter' package — "
"use an 'every' or 'at' schedule instead"
) from None
except Exception as exc:
raise ValueError(f"invalid cron expression {schedule.expr!r}: {exc}") from None
return None
def validate_schedule(schedule: CronSchedule) -> None:
"""Reject schedules that could never run (raises ValueError)."""
if schedule.kind == "at":
if not schedule.at_ms:
raise ValueError("'at' schedules need a time")
if schedule.at_ms <= _now_ms():
raise ValueError("'at' time is in the past")
return
if schedule.kind == "every":View on GitHub (pinned to 3e82f13042)
Solutions
- pip install croniter (or add it to project deps / extras group)
- Switch the schedule to kind='every' with every_seconds or kind='at' with at_ms, which need no extra dependency
Example fix
# before schedule = CronSchedule(kind="cron", expr="0 9 * * *") service.add_job(schedule=schedule, ...) # ValueError: needs croniter # after pip install croniter # or without croniter: schedule = CronSchedule(kind="every", every_seconds=86400)
Defensive patterns
Strategy: validation
Validate before calling
def croniter_available() -> bool:
try:
import croniter # noqa: F401
return True
except ImportError:
return False
if schedule.kind == "cron" and not croniter_available():
schedule = CronSchedule(kind="every", every_seconds=3600) Try / catch
try:
svc.add_job(schedule, ...)
except ValueError as e:
if "croniter" in str(e):
schedule = CronSchedule(kind="every", every_seconds=fallback_seconds)
svc.add_job(schedule, ...)
else:
raise Prevention
- Add croniter to deployment dependency checks/startup doctor
- Advertise capability: only expose cron expressions in the UI when croniter is importable
When it happens
Trigger: Creating CronSchedule(kind="cron", expr="*/5 * * * *") and calling add_job/validate_schedule/compute_next_run where croniter is not installed.
Common situations: Slim Docker images or serverless envs omitting the optional extra; upgrading the package without reinstalling extras; CI/prod dependency drift.
Related errors
- invalid cron expression {schedule.expr!r}: {exc}
- 'every' interval must be at least 30 seconds
- AnimationGenerator requires the optional math-animator extra
- 'at' schedules need a time
- 'at' time is in the past
AI-assisted analysis of HKUDS/DeepTutor@3e82f13042 (2026-08-27).
Data as JSON: /api/errors/7093b8db32ded81c.
Report an issue: GitHub.