{"record":{"id":"78db4a5c4a9fd15e","repo":"HKUDS/DeepTutor","slug":"unknown-schedule-kind-schedule-kind-r","errorCode":null,"errorMessage":"unknown schedule kind {schedule.kind!r}","messagePattern":"unknown schedule kind (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"deeptutor/services/cron/service.py","lineNumber":175,"sourceCode":"            raise ValueError(\"'at' time is in the past\")\n        return\n    if schedule.kind == \"every\":\n        if not schedule.every_seconds or schedule.every_seconds < 30:\n            raise ValueError(\"'every' interval must be at least 30 seconds\")\n        return\n    if schedule.kind == \"cron\":\n        if schedule.tz:\n            try:\n                from zoneinfo import ZoneInfo\n\n                ZoneInfo(schedule.tz)\n            except Exception:\n                raise ValueError(f\"unknown timezone {schedule.tz!r}\") from None\n        # Raises ValueError on bad/unsupported expressions.\n        if compute_next_run(schedule, _now_ms()) is None:\n            raise ValueError(f\"cron expression {schedule.expr!r} never fires\")\n        return\n    raise ValueError(f\"unknown schedule kind {schedule.kind!r}\")\n\n\nclass CronService:\n    \"\"\"Single-process job store + scheduler.\"\"\"\n\n    def __init__(\n        self,\n        store_path: Path,\n        on_job: Callable[[CronJob], Awaitable[tuple[str, str | None]]] | None = None,\n    ) -> None:\n        \"\"\"``on_job`` returns ``(status, error)`` with status ok/error/skipped.\"\"\"\n        self.store_path = store_path\n        self.on_job = on_job\n        self._jobs: dict[str, CronJob] = {}\n        self._loaded = False\n        self._timer_task: asyncio.Task | None = None\n        self._wake = asyncio.Event()\n        self._running = False","sourceCodeStart":157,"sourceCodeEnd":193,"githubUrl":"https://github.com/HKUDS/DeepTutor/blob/3e82f130422a813cdd73c10b21a44e9325f5821a/deeptutor/services/cron/service.py#L157-L193","documentation":"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.","triggerScenarios":"CronSchedule(kind=\"daily\"), kind=\"Once\", or a deserialized record with an empty/renamed kind passed to add_job.","commonSituations":"Schema evolution renaming kinds; typos from config files or user input; old persisted jobs replayed against a newer service that dropped a kind.","solutions":["Use one of the exact kinds: 'at', 'every', or 'cron'","Map legacy kind names to the current three during migration before calling add_job","Validate kind at the API boundary and return 400 with the allowed values"],"exampleFix":"# before\nsched = CronSchedule(kind=\"daily\", every_seconds=86400)  # ValueError\n# after\nsched = CronSchedule(kind=\"every\", every_seconds=86400)\n# migration:\nLEGACY = {\"daily\": \"every\", \"once\": \"at\", \"recurring\": \"every\"}\nkind = LEGACY.get(raw_kind, raw_kind)","handlingStrategy":"type-guard","validationCode":"ALLOWED_KINDS = {\"at\", \"every\", \"cron\"}\nassert schedule.kind in ALLOWED_KINDS, f\"kind must be one of {ALLOWED_KINDS}\"","typeGuard":"def is_known_schedule_kind(s: CronSchedule) -> bool:\n    return s.kind in {\"at\", \"every\", \"cron\"}","tryCatchPattern":"try:\n    svc.add_job(schedule, ...)\nexcept ValueError as e:\n    if \"unknown schedule kind\" in str(e):\n        return bad_request(f\"kind must be at/every/cron, got {schedule.kind!r}\")\n    raise","preventionTips":["Validate kind with a Literal/enum at your API schema","Map legacy kind names during migrations"],"tags":["cron","validation","enum"],"backgroundTag":"invalid-enum-value","analyzedSha":"3e82f130422a813cdd73c10b21a44e9325f5821a","analyzedAt":"2026-08-27T06:57:25.364Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}