HKUDS/DeepTutor · error · ValueError

unknown schedule kind {schedule.kind!r}

Error message

unknown schedule kind {schedule.kind!r}

What it means

validate_schedule ends with a fallthrough raise for any schedule kind other than 'at', 'every', and 'cron'. kind is an open string, so a typo, legacy, or future kind is rejected with the offending value in the message.

Source

Thrown at deeptutor/services/cron/service.py:175

            raise ValueError("'at' time is in the past")
        return
    if schedule.kind == "every":
        if not schedule.every_seconds or schedule.every_seconds < 30:
            raise ValueError("'every' interval must be at least 30 seconds")
        return
    if schedule.kind == "cron":
        if schedule.tz:
            try:
                from zoneinfo import ZoneInfo

                ZoneInfo(schedule.tz)
            except Exception:
                raise ValueError(f"unknown timezone {schedule.tz!r}") from None
        # Raises ValueError on bad/unsupported expressions.
        if compute_next_run(schedule, _now_ms()) is None:
            raise ValueError(f"cron expression {schedule.expr!r} never fires")
        return
    raise ValueError(f"unknown schedule kind {schedule.kind!r}")


class CronService:
    """Single-process job store + scheduler."""

    def __init__(
        self,
        store_path: Path,
        on_job: Callable[[CronJob], Awaitable[tuple[str, str | None]]] | None = None,
    ) -> None:
        """``on_job`` returns ``(status, error)`` with status ok/error/skipped."""
        self.store_path = store_path
        self.on_job = on_job
        self._jobs: dict[str, CronJob] = {}
        self._loaded = False
        self._timer_task: asyncio.Task | None = None
        self._wake = asyncio.Event()
        self._running = False

View on GitHub (pinned to 3e82f13042)

Solutions

  1. Use one of the exact kinds: 'at', 'every', or 'cron'
  2. Map legacy kind names to the current three during migration before calling add_job
  3. Validate kind at the API boundary and return 400 with the allowed values

Example fix

# before
sched = CronSchedule(kind="daily", every_seconds=86400)  # ValueError
# after
sched = CronSchedule(kind="every", every_seconds=86400)
# migration:
LEGACY = {"daily": "every", "once": "at", "recurring": "every"}
kind = LEGACY.get(raw_kind, raw_kind)
Defensive patterns

Strategy: type-guard

Validate before calling

ALLOWED_KINDS = {"at", "every", "cron"}
assert schedule.kind in ALLOWED_KINDS, f"kind must be one of {ALLOWED_KINDS}"

Type guard

def is_known_schedule_kind(s: CronSchedule) -> bool:
    return s.kind in {"at", "every", "cron"}

Try / catch

try:
    svc.add_job(schedule, ...)
except ValueError as e:
    if "unknown schedule kind" in str(e):
        return bad_request(f"kind must be at/every/cron, got {schedule.kind!r}")
    raise

Prevention

When it happens

Trigger: CronSchedule(kind="daily"), kind="Once", or a deserialized record with an empty/renamed kind passed to add_job.

Common situations: Schema evolution renaming kinds; typos from config files or user input; old persisted jobs replayed against a newer service that dropped a kind.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


AI-assisted analysis of HKUDS/DeepTutor@3e82f13042 (2026-08-27). Data as JSON: /api/errors/78db4a5c4a9fd15e. Report an issue: GitHub.