{"record":{"id":"fe0abddf297e0122","repo":"HKUDS/DeepTutor","slug":"every-interval-must-be-at-least-30-seconds","errorCode":null,"errorMessage":"'every' interval must be at least 30 seconds","messagePattern":"'every' interval must be at least 30 seconds","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"deeptutor/services/cron/service.py","lineNumber":161,"sourceCode":"                \"use an 'every' or 'at' schedule instead\"\n            ) from None\n        except Exception as exc:\n            raise ValueError(f\"invalid cron expression {schedule.expr!r}: {exc}\") from None\n\n    return None\n\n\ndef validate_schedule(schedule: CronSchedule) -> None:\n    \"\"\"Reject schedules that could never run (raises ValueError).\"\"\"\n    if schedule.kind == \"at\":\n        if not schedule.at_ms:\n            raise ValueError(\"'at' schedules need a time\")\n        if schedule.at_ms <= _now_ms():\n            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.\"\"\"","sourceCodeStart":143,"sourceCodeEnd":179,"githubUrl":"https://github.com/HKUDS/DeepTutor/blob/3e82f130422a813cdd73c10b21a44e9325f5821a/deeptutor/services/cron/service.py#L143-L179","documentation":"validate_schedule enforces a 30-second minimum for 'every' (interval) schedules: every_seconds must be truthy and >= 30. The floor prevents tight-loop jobs from hammering the single-process scheduler and downstream LLM endpoints.","triggerScenarios":"CronSchedule(kind=\"every\", every_seconds=10) or every_seconds=None/0 passed to add_job/validate_schedule.","commonSituations":"Prototyping with a 5s interval to observe firing; tests wanting fast ticks; UI allowing sub-30 values; unit confusion (minutes vs seconds).","solutions":["Raise the interval to at least 30 seconds","For tests, mock the scheduler clock rather than shipping a sub-30 production config","Double-check units — 30 means 30 seconds, not 30 minutes"],"exampleFix":"# before\nsched = CronSchedule(kind=\"every\", every_seconds=5)  # ValueError\n# after\nsched = CronSchedule(kind=\"every\", every_seconds=30)  # minimum allowed","handlingStrategy":"validation","validationCode":"MIN_EVERY_SECONDS = 30\ndef valid_every(s: CronSchedule) -> bool:\n    return s.kind != \"every\" or (bool(s.every_seconds) and s.every_seconds >= MIN_EVERY_SECONDS)","typeGuard":null,"tryCatchPattern":"try:\n    svc.add_job(schedule, ...)\nexcept ValueError as e:\n    if \"at least 30 seconds\" in str(e):\n        schedule.every_seconds = 30\n        svc.add_job(schedule, ...)\n    else:\n        raise","preventionTips":["Clamp interval inputs to >= 30 in the UI","State units explicitly (seconds) in config schemas","In tests, mock the clock instead of shrinking intervals"],"tags":["cron","validation","rate-limit","scheduling"],"backgroundTag":"interval-below-minimum","analyzedSha":"3e82f130422a813cdd73c10b21a44e9325f5821a","analyzedAt":"2026-08-27T06:57:25.364Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}